using System.Diagnostics;
namespace laba3.Subprograms
{
///
/// TUI classic game "Snake";
/// The player controls the snake.
/// The player has to eat apples to increase the length of the snake
/// and try not to crash into the wall or themselves;
///
internal class SnakeGame
{
int size_x;
int size_y;
int score;
int size_xy;
Level difficulty;
Tiles[,] world;
Snake snake;
Random rnd;
private bool gameover;
///
/// Game world constructor;
///
/// Game speed
/// Game world size dim x
/// Game world size dim y
public SnakeGame(Level difficulty, int size_x, int size_y)
{
this.size_x = size_x;
this.size_y = size_y;
this.difficulty = difficulty;
world = new Tiles[size_x, size_y];
rnd = new Random();
score = 0;
size_xy = size_x * size_y;
}
///
/// Start game;
/// Console will be clean!;
///
public void start()
{
int msPerTick = difficulty switch
{
Level.Low => 500,
Level.Medium => 250,
Level.High => 100,
_ => 500,
};
gameover = false;
Console.Clear();
Console.CursorVisible = false;
snake = new Snake(new Point(size_x / 2, size_y / 2, this), 1, Direction.Right, this);
GenerateFood();
DrawBackground();
DrawWorld();
Stopwatch tickTimer = new Stopwatch();
while (!gameover)
{
tickTimer.Restart();
if (Console.KeyAvailable)
{
ConsoleKeyInfo key = Console.ReadKey(false);
snake.HandleKey(key.Key);
while (Console.KeyAvailable)
Console.ReadKey(false);
}
snake.Move();
long delta = tickTimer.ElapsedMilliseconds;
Console.SetCursorPosition(1, 0);
if (delta > msPerTick)
Console.ForegroundColor = ConsoleColor.Red;
Console.Write(delta);
Thread.Sleep((int)Math.Max(0, msPerTick - delta));
}
onStop();
}
///
/// Stop game;
///
public void stop()
{
gameover = true;
}
private void onStop()
{
string gameOverTitle =
@"
### ##
# ## #### # # # # # # ##
# ## # # # # # ### # # # # ### #
# # # # # # # # # # # # #
### ## # # # ## ## # ## #
";
Console.Clear();
printArt(gameOverTitle, (Console.WindowWidth - 36) / 2, 4, ConsoleColor.DarkYellow, ConsoleColor.DarkGray);
Console.ResetColor();
string scoreString = $"Score: {score}";
Console.SetCursorPosition((Console.WindowWidth - scoreString.Length) / 2, 11);
Console.Write(scoreString);
Console.SetCursorPosition((Console.WindowWidth - "Press Enter to continue".Length) / 2, 14);
Console.CursorVisible = true;
}
private void printArt(string art, int x, int y, ConsoleColor primaryColor, ConsoleColor shadowColor)
{
int row = 0;
Console.SetCursorPosition(x, y);
char shadow = ' ';
foreach (char ch in art)
{
if (ch == '\n')
{
if (shadow == '#')
{
Console.BackgroundColor = shadowColor;
Console.Write(' ');
}
Console.SetCursorPosition(x, y++ + row);
Thread.Sleep(25);
shadow = ch;
}
else if (ch == ' ')
{
if (shadow == '#')
{
Console.BackgroundColor = shadowColor;
Console.Write(' ');
}
else
Console.CursorLeft++;
shadow = ch;
}
else if (ch == '#')
{
Console.BackgroundColor = primaryColor;
Console.Write(' ');
shadow = ch;
}
}
}
private void addScore()
{
score += difficulty switch
{
Level.Low => 10,
Level.Medium => 50,
Level.High => 100,
_ => 1,
};
}
///
/// Generate food in random place;
///
public void GenerateFood()
{
int x;
int y;
do
{
x = rnd.Next(size_x);
y = rnd.Next(size_y);
} while (world[x, y] != Tiles.Void);
world[x, y] = Tiles.Food;
DrawTile(x, y);
}
private void DrawBackground()
{
for (int x = 1; x < (size_x + 1) * 2 + 1; x++)
{
Console.SetCursorPosition(x, 1);
Console.Write('#');
Console.SetCursorPosition(x, size_y + 2);
Console.Write('#');
}
for (int y = 1; y < size_y + 2; y++)
{
Console.SetCursorPosition(1, y);
Console.Write('#');
Console.SetCursorPosition(size_x * 2 + 1, y);
Console.Write('#');
}
}
private void DrawTile(int x, int y)
{
char symbol;
switch (world[x, y])
{
case Tiles.Void:
symbol = ' ';
break;
case Tiles.Snake:
symbol = '*';
Console.ForegroundColor = ConsoleColor.DarkRed;
break;
case Tiles.Food:
symbol = '@';
Console.ForegroundColor = ConsoleColor.Red;
break;
default:
symbol = '?';
break;
};
//Console.BackgroundColor = ConsoleColor.DarkGreen;
Console.SetCursorPosition((x + 1) * 2, y + 2);
Console.Write(symbol);
Console.BackgroundColor = ConsoleColor.Black;
Console.ForegroundColor = ConsoleColor.White;
}
private void DrawWorld()
{
for (int x = 0; x < size_x; x++)
{
for (int y = 0; y < size_y; y++)
{
DrawTile(x, y);
}
}
}
private enum Tiles
{
Void,
Snake,
Food
}
public enum Level
{
Low,
Medium,
High
}
private class Snake
{
private SnakeGame game;
private List body;
private Direction direction;
public Snake(Point tail, int length, Direction initialDirection, SnakeGame game)
{
this.game = game;
body = new List();
direction = initialDirection;
for (int i = 0; i < length; i++)
{
Point p = new Point(tail.X, tail.Y, game);
body.Add(p);
}
}
public void Move()
{
Point head = GetNextPoint();
Point tail = body.First();
if (head.X < 0 || head.Y < 0 || head.X >= game.size_x || head.Y >= game.size_y)
{
game.stop();
return;
}
if (game.world[head.X, head.Y] == Tiles.Snake)
{
game.stop();
return;
}
body.Add(head);
if (game.world[head.X, head.Y] != Tiles.Food)
{
body.Remove(tail);
tail.UpdateWorld(Tiles.Void);
}
else
{
game.addScore();
game.GenerateFood();
if (body.Count >= game.size_xy) game.stop();
}
head.UpdateWorld(Tiles.Snake);
}
public Point GetNextPoint()
{
Point head = body.Last();
Point nextPoint = new Point(head.X, head.Y, game);
switch (direction)
{
case Direction.Right:
nextPoint.X++;
break;
case Direction.Left:
nextPoint.X--;
break;
case Direction.Up:
nextPoint.Y--;
break;
case Direction.Down:
nextPoint.Y++;
break;
}
return nextPoint;
}
public void HandleKey(ConsoleKey key)
{
switch (key)
{
case ConsoleKey.LeftArrow:
if (direction != Direction.Right)
direction = Direction.Left;
break;
case ConsoleKey.RightArrow:
if (direction != Direction.Left)
direction = Direction.Right;
break;
case ConsoleKey.UpArrow:
if (direction != Direction.Down)
direction = Direction.Up;
break;
case ConsoleKey.DownArrow:
if (direction != Direction.Up)
direction = Direction.Down;
break;
}
}
}
enum Direction
{
Left,
Right,
Up,
Down
}
private class Point
{
private SnakeGame game { get; set; }
public int X { get; set; }
public int Y { get; set; }
public Point(int x, int y, SnakeGame game)
{
this.game = game;
X = x;
Y = y;
}
public void UpdateWorld(Tiles tile)
{
game.world[X, Y] = tile;
game.DrawTile(X, Y);
}
}
}
}