using System; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.Linq; | |
namespace Snake | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
var board = new Board(40, 20); | |
var sw = Stopwatch.StartNew(); | |
while (true) | |
{ | |
while (Console.KeyAvailable) | |
{ | |
var key = Console.ReadKey(true); | |
Direction direction = Direction.None; | |
switch (key.Key) | |
{ | |
case ConsoleKey.UpArrow: | |
direction = Direction.Up; | |
break; | |
case ConsoleKey.DownArrow: | |
direction = Direction.Down; | |
break; | |
case ConsoleKey.LeftArrow: | |
direction = Direction.Left; | |
break; | |
case ConsoleKey.RightArrow: | |
direction = Direction.Right; | |
break; | |
} | |
board.SetMove(direction); | |
} | |
if (sw.ElapsedMilliseconds > 300) | |
{ | |
if (board.MoveByOneField()) | |
break; | |
sw.Restart(); | |
} | |
} | |
Console.SetCursorPosition(16, 9); | |
Console.ForegroundColor = ConsoleColor.Red; | |
Console.WriteLine("GAME OVER"); | |
Console.SetCursorPosition(0, 20); | |
Console.ReadLine(); | |
} | |
} | |
public class Board | |
{ | |
private FieldValue[] boardState; | |
private Direction[] boardDirections; | |
private int width; | |
private int height; | |
private int snakeHead; | |
private int snakeTail; | |
private Direction currentDirection = Direction.Right; | |
public Board(int width, int height) | |
{ | |
this.width = width; | |
this.height = height; | |
boardDirections = new Direction[width * height]; | |
boardState = GenerateEmptyBoard(); | |
AddSnake(); | |
AddFood(); | |
PrintBoard(); | |
} | |
public void SetMove(Direction direction) | |
{ | |
boardDirections[snakeHead] = direction; | |
currentDirection = direction; | |
} | |
public bool MoveByOneField() | |
{ | |
if (boardDirections[snakeHead] == Direction.None) | |
boardDirections[snakeHead] = currentDirection; | |
var fieldsToRefresh = new List<int>(); | |
var nextHeadPosition = GetNextFieldId(snakeHead); | |
var nextHeadField = boardState[nextHeadPosition]; | |
switch (nextHeadField) | |
{ | |
case FieldValue.Snake: | |
case FieldValue.Wall: | |
return true; | |
case FieldValue.Food: | |
boardDirections[nextHeadPosition] = boardDirections[snakeHead]; | |
boardState[nextHeadPosition] = FieldValue.Snake; | |
fieldsToRefresh.Add(snakeHead); | |
fieldsToRefresh.Add(nextHeadPosition); | |
snakeHead = nextHeadPosition; | |
fieldsToRefresh.Add(AddFood()); | |
break; | |
case FieldValue.Empty: | |
fieldsToRefresh = MoveSnake().ToList(); | |
break; | |
} | |
RefreshFieldsOnScreen(fieldsToRefresh); | |
return false; | |
} | |
private void AddSnake() | |
{ | |
snakeHead = width * ((height / 2)-1) + width / 2 + 1; | |
snakeTail = snakeHead - 2; | |
boardState[snakeHead] = FieldValue.Snake; | |
boardState[snakeHead - 1] = FieldValue.Snake; | |
boardState[snakeHead - 2] = FieldValue.Snake; | |
boardDirections[snakeHead] = currentDirection; | |
boardDirections[snakeHead - 1] = currentDirection; | |
boardDirections[snakeHead - 2] = currentDirection; | |
} | |
private IEnumerable<int> MoveSnake() | |
{ | |
var newBoard = GenerateEmptyBoard(); | |
int newSnakeHead = 0; | |
int newSnakeTail = 0; | |
for (var i = 0; i < boardState.Length; i++) | |
{ | |
if (boardState[i] == FieldValue.Snake) | |
{ | |
var nextField = GetNextFieldId(i); | |
newBoard[nextField] = FieldValue.Snake; | |
if (i == snakeHead) | |
newSnakeHead = nextField; | |
if (i == snakeTail) | |
{ | |
boardDirections[i] = Direction.None; | |
newSnakeTail = nextField; | |
} | |
yield return i; | |
yield return nextField; | |
} | |
else if (boardState[i] == FieldValue.Food) | |
{ | |
newBoard[i] = FieldValue.Food; | |
} | |
} | |
boardState = newBoard; | |
snakeHead = newSnakeHead; | |
snakeTail = newSnakeTail; | |
} | |
private FieldValue[] GenerateEmptyBoard() | |
{ | |
var newBoardState = new FieldValue[width * height]; | |
for (var i = 0; i < width; i++) | |
newBoardState[i] = FieldValue.Wall; | |
for (var i = (height - 1) * width; i < newBoardState.Length; i++) | |
newBoardState[i] = FieldValue.Wall; | |
for (var i = 1; i < height; i++) | |
{ | |
newBoardState[i * width] = FieldValue.Wall; | |
newBoardState[i * width - 1] = FieldValue.Wall; | |
} | |
return newBoardState; | |
} | |
private int GetNextFieldId(int currentFieldId) | |
{ | |
var direction = boardDirections[currentFieldId]; | |
switch (direction) | |
{ | |
case Direction.Right: | |
return currentFieldId + 1; | |
case Direction.Left: | |
return currentFieldId - 1; | |
case Direction.Up: | |
return currentFieldId - width; | |
case Direction.Down: | |
return currentFieldId + width; | |
} | |
return -1; | |
} | |
private int AddFood() | |
{ | |
var rnd = new Random(); | |
while (true) | |
{ | |
var fieldId = rnd.Next(boardState.Length); | |
if (boardState[fieldId] == FieldValue.Empty) | |
{ | |
boardState[fieldId] = FieldValue.Food; | |
return fieldId; | |
} | |
} | |
} | |
public void RefreshFieldsOnScreen(IEnumerable<int> fieldsToRefresh) | |
{ | |
foreach (var fieldId in fieldsToRefresh) | |
{ | |
var row = fieldId / width; | |
var column = fieldId % width; | |
Console.SetCursorPosition(column, row); | |
var fieldValue = boardState[fieldId]; | |
PrintField(fieldValue, fieldId == snakeHead); | |
} | |
Console.SetCursorPosition(0, height); | |
} | |
private void PrintBoard() | |
{ | |
Console.Clear(); | |
for (var h = 0; h < height; h++) | |
{ | |
for (var w = 0; w < width; w++) | |
{ | |
var fieldId = h * width + w; | |
var fieldValue = boardState[fieldId]; | |
PrintField(fieldValue, fieldId == snakeHead); | |
} | |
Console.WriteLine(); | |
} | |
} | |
private void PrintField(FieldValue fieldValue, bool isHead) | |
{ | |
switch (fieldValue) | |
{ | |
case FieldValue.Wall: | |
Console.BackgroundColor = ConsoleColor.DarkRed; | |
Console.Write(' '); | |
Console.ResetColor(); | |
break; | |
case FieldValue.Snake: | |
Console.BackgroundColor = isHead ? ConsoleColor.DarkYellow : ConsoleColor.DarkGreen; | |
Console.Write(' '); | |
Console.ResetColor(); | |
break; | |
case FieldValue.Food: | |
Console.BackgroundColor = ConsoleColor.White; | |
Console.Write(' '); | |
Console.ResetColor(); | |
break; | |
case FieldValue.Empty: | |
Console.BackgroundColor = ConsoleColor.Green; | |
Console.Write(' '); | |
Console.ResetColor(); | |
break; | |
} | |
} | |
} | |
public enum FieldValue : byte | |
{ | |
Empty, | |
Wall, | |
Snake, | |
Food | |
} | |
public enum Direction : byte | |
{ | |
None, | |
Up, | |
Down, | |
Left, | |
Right | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment