Last active
July 11, 2017 21:17
Star
You must be signed in to star a gist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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