2048 console game (C#)

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
5
down vote

favorite
3












So about a week ago I decided to write a simple console version of 2048 game. Well, as you'll see, it came out not that simple... And took a lot more time and practice than expected (should've done it with 2d array...). But anyway here's a code, and I want to know how can it be optimized and improved? I want to know my mistakes and how it can be done easier. If you have some general tips on coding style, readability or anything else I'd appreciate your help.



using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace Game

class Program

static void Main()

Console.CursorVisible = false;

do

Game game = new Game(4, 8, 2);

string key;
do

Console.WriteLine("1. New game");
Console.WriteLine("2. Quit");
key = Console.ReadLine();
Console.Clear();
while (key != "1" && key != "2");

switch (key)

case "1":
game.Run();
break;
case "2":
Environment.Exit(0);
break;

Console.Clear();
while (true);



public enum Direction

Left,
Right,
Up,
Down


class Cell : IEquatable<Cell>, ICloneable

public int Value get; set;
public int X get; set;
public int Y get; set;

public Cell(int value, int x, int y)

Value = value;
X = x;
Y = y;


// Double the value
public void Double() => Value *= 2;

// Display value of cell at certain coordinates
public void Display()

Console.SetCursorPosition((Y - 1) * 5 + 1, (X - 1) * 2 + 1);
ColorChanger.ColorCell(Value.ToString());


// Erase value of cell at certain coordinates
public void Erase()

Console.SetCursorPosition((Y - 1) * 5 + 1, (X - 1) * 2 + 1);
ColorChanger.ColorCell(" ");


#region overriding section

public static bool operator ==(Cell cell1, Cell cell2)

return cell1.Value == cell2.Value && cell1.X == cell2.X && cell1.Y == cell2.Y;


public static bool operator !=(Cell cell1, Cell cell2)

return !(cell1.Value == cell2.Value && cell1.X == cell2.X && cell1.Y == cell2.Y);


public bool Equals(Cell cell)

if (cell is null)
return false;

return Value == cell.Value && X == cell.X && Y == cell.Y;


public override bool Equals(object obj)

Cell cell = obj as Cell;
if (obj == null)
return false;
return Value == cell.Value && X == cell.X && Y == cell.Y;


public override int GetHashCode()

return Value.GetHashCode() ^ X.GetHashCode() ^ Y.GetHashCode();


public object Clone() => MemberwiseClone();

#endregion


class Game

// Length & Width of game field (doesn't have to be equal)
int length;
int width;

int Length

get => length;
set

if (value >= 2)
length = value;
else
length = 2;


int Width

get => width;
set

if (value >= 2)
width = value;
else
width = 2;



// Stats of current game
int Score get; set;
static int Highscore get; set;
int Moves get; set;

// List of current cells & cells on previous move
List<Cell> cells;
List<Cell> prevCells;

Random rand = new Random();

// Initializing the game with field parameters and cell amount at the beginning
public Game(int length, int width, int initialCellAmount)

cells = new List<Cell>();
prevCells = new List<Cell>();
Length = length;
Width = width;
Score = 0;
Moves = 0;

for (int i = 0; i < initialCellAmount; i++)
AddCell();


// Launching the game
public void Run()

DisplayField();
DisplayCells();
DisplayStats();

do

if (Console.KeyAvailable)

while (true);


// Checking for any cell on field that moved
bool IsMoved()

foreach (Cell cell in prevCells)

if (cells.Contains(cell) == false)

Moves++;
return true;


return false;


// Adding new cell on the field (90% - '2', 10% - '4')
void AddCell()

if (IsFieldFull())
return;
Cell cell;
do

cell = new Cell(rand.NextDouble() < 0.9 ? 2 : 4, rand.Next(1, Length), rand.Next(1, Width));
while (cells.Any(c => c.X == cell.X && c.Y == cell.Y));
cells.Add(cell);


// Drawing the field
void DisplayField()

for (int i = 0; i < Length + 1; i++)
");

Console.WriteLine();



// Displaying existing cells
void DisplayCells()

foreach (Cell cell in cells)
cell.Display();


// Displaying score, highscore and moves
void DisplayStats()

Console.SetCursorPosition(0, 9);
Console.WriteLine($"Score: Score Highscore: Highscoren" +
$"Moves: Moves");


// Cells movement algorithm
void MoveCells(Direction dir)
dir == Direction.Up ? 1 : -1;

for (int i = -1; i + 1 < cells.Count(); i++)
getCoord(getCurr(), true) != getCoord(getNext(), true))

Cell temp = getNext();
setCoord(ref temp, border);
continue;


if (getCurr().Value == getNext().Value)

getCurr().Double();
CalculateScore(getCurr().Value);
cells.Remove(getNext());
i--;

else

Cell temp = getNext();
setCoord(ref temp, getCoord(getCurr(), false) + n);




// Setting new score and highscore
void CalculateScore(int value)

Score += value;
if (Score > Highscore)
Highscore = Score;


// Choosing movement direction of corresponding key
void HandleKey()

prevCells = cells.Select(c => (Cell)c.Clone()).ToList();
ConsoleKeyInfo cki = Console.ReadKey(true);
switch (cki.Key)

case ConsoleKey.LeftArrow:
MoveCells(Direction.Left);
break;
case ConsoleKey.RightArrow:
MoveCells(Direction.Right);
break;
case ConsoleKey.UpArrow:
MoveCells(Direction.Up);
break;
case ConsoleKey.DownArrow:
MoveCells(Direction.Down);
break;



// Checking for field overflow
bool IsFieldFull()

return cells.Count() == Length * Width;


// Checking if there is no cell that can be moved
bool IsOver()

if (!IsFieldFull())
return false;

cells = cells.OrderBy(c => c.X).ThenBy(c => c.Y).ToList();

int[,] values = new int[Length, Width];

foreach (Cell cell in cells)

values[cell.X - 1, cell.Y - 1] = cell.Value;


for (int i = 0; i < Length; i++)

for (int j = 0; j < Width; j++)
center == bottom)
return false;


return true;


// Searching for cell with 2048
bool IsWon()

return cells.Any(c => c.Value == 2048);



// Class for changing color for cells
static class ColorChanger

// Get color for corresponding value of cell
static ConsoleColor GetCellColor(string value)

switch (value)

case "2":
return ConsoleColor.Blue;
case "4":
return ConsoleColor.Magenta;
case "8":
return ConsoleColor.Cyan;
case "16":
return ConsoleColor.Green;
case "32":
return ConsoleColor.Yellow;
case "64":
return ConsoleColor.DarkBlue;
case "128":
return ConsoleColor.DarkMagenta;
case "256":
return ConsoleColor.DarkCyan;
case "512":
return ConsoleColor.DarkGreen;
case "1024":
return ConsoleColor.DarkYellow;
default:
return ConsoleColor.Red;



// Color certain cell with optional background color
public static void ColorCell(string value, ConsoleColor bgColor = ConsoleColor.Black)

ConsoleColor defaultFg = Console.ForegroundColor;
ConsoleColor defaultBg = Console.BackgroundColor;

Console.ForegroundColor = GetCellColor(value);
Console.BackgroundColor = bgColor;

Console.WriteLine(value);

Console.ForegroundColor = defaultFg;
Console.BackgroundColor = defaultBg;









share|improve this question

















  • 2




    Bravo, well done! The code is very readable and well structured.
    – Olivier Jacot-Descombes
    Feb 24 at 15:26










  • @OlivierJacot-Descombes, thanks, but aside of that I still think there is a room for improvement :)
    – Glitch
    Feb 24 at 15:33











  • The code is very readable and well structured. you think? I'm not so sure about it...
    – t3chb0t
    Feb 25 at 15:30










  • @t3chb0t then provide some clarification, what you think is wrong and needs to be improved?
    – Glitch
    Feb 25 at 15:42
















up vote
5
down vote

favorite
3












So about a week ago I decided to write a simple console version of 2048 game. Well, as you'll see, it came out not that simple... And took a lot more time and practice than expected (should've done it with 2d array...). But anyway here's a code, and I want to know how can it be optimized and improved? I want to know my mistakes and how it can be done easier. If you have some general tips on coding style, readability or anything else I'd appreciate your help.



using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace Game

class Program

static void Main()

Console.CursorVisible = false;

do

Game game = new Game(4, 8, 2);

string key;
do

Console.WriteLine("1. New game");
Console.WriteLine("2. Quit");
key = Console.ReadLine();
Console.Clear();
while (key != "1" && key != "2");

switch (key)

case "1":
game.Run();
break;
case "2":
Environment.Exit(0);
break;

Console.Clear();
while (true);



public enum Direction

Left,
Right,
Up,
Down


class Cell : IEquatable<Cell>, ICloneable

public int Value get; set;
public int X get; set;
public int Y get; set;

public Cell(int value, int x, int y)

Value = value;
X = x;
Y = y;


// Double the value
public void Double() => Value *= 2;

// Display value of cell at certain coordinates
public void Display()

Console.SetCursorPosition((Y - 1) * 5 + 1, (X - 1) * 2 + 1);
ColorChanger.ColorCell(Value.ToString());


// Erase value of cell at certain coordinates
public void Erase()

Console.SetCursorPosition((Y - 1) * 5 + 1, (X - 1) * 2 + 1);
ColorChanger.ColorCell(" ");


#region overriding section

public static bool operator ==(Cell cell1, Cell cell2)

return cell1.Value == cell2.Value && cell1.X == cell2.X && cell1.Y == cell2.Y;


public static bool operator !=(Cell cell1, Cell cell2)

return !(cell1.Value == cell2.Value && cell1.X == cell2.X && cell1.Y == cell2.Y);


public bool Equals(Cell cell)

if (cell is null)
return false;

return Value == cell.Value && X == cell.X && Y == cell.Y;


public override bool Equals(object obj)

Cell cell = obj as Cell;
if (obj == null)
return false;
return Value == cell.Value && X == cell.X && Y == cell.Y;


public override int GetHashCode()

return Value.GetHashCode() ^ X.GetHashCode() ^ Y.GetHashCode();


public object Clone() => MemberwiseClone();

#endregion


class Game

// Length & Width of game field (doesn't have to be equal)
int length;
int width;

int Length

get => length;
set

if (value >= 2)
length = value;
else
length = 2;


int Width

get => width;
set

if (value >= 2)
width = value;
else
width = 2;



// Stats of current game
int Score get; set;
static int Highscore get; set;
int Moves get; set;

// List of current cells & cells on previous move
List<Cell> cells;
List<Cell> prevCells;

Random rand = new Random();

// Initializing the game with field parameters and cell amount at the beginning
public Game(int length, int width, int initialCellAmount)

cells = new List<Cell>();
prevCells = new List<Cell>();
Length = length;
Width = width;
Score = 0;
Moves = 0;

for (int i = 0; i < initialCellAmount; i++)
AddCell();


// Launching the game
public void Run()

DisplayField();
DisplayCells();
DisplayStats();

do

if (Console.KeyAvailable)

while (true);


// Checking for any cell on field that moved
bool IsMoved()

foreach (Cell cell in prevCells)

if (cells.Contains(cell) == false)

Moves++;
return true;


return false;


// Adding new cell on the field (90% - '2', 10% - '4')
void AddCell()

if (IsFieldFull())
return;
Cell cell;
do

cell = new Cell(rand.NextDouble() < 0.9 ? 2 : 4, rand.Next(1, Length), rand.Next(1, Width));
while (cells.Any(c => c.X == cell.X && c.Y == cell.Y));
cells.Add(cell);


// Drawing the field
void DisplayField()

for (int i = 0; i < Length + 1; i++)
");

Console.WriteLine();



// Displaying existing cells
void DisplayCells()

foreach (Cell cell in cells)
cell.Display();


// Displaying score, highscore and moves
void DisplayStats()

Console.SetCursorPosition(0, 9);
Console.WriteLine($"Score: Score Highscore: Highscoren" +
$"Moves: Moves");


// Cells movement algorithm
void MoveCells(Direction dir)
dir == Direction.Up ? 1 : -1;

for (int i = -1; i + 1 < cells.Count(); i++)
getCoord(getCurr(), true) != getCoord(getNext(), true))

Cell temp = getNext();
setCoord(ref temp, border);
continue;


if (getCurr().Value == getNext().Value)

getCurr().Double();
CalculateScore(getCurr().Value);
cells.Remove(getNext());
i--;

else

Cell temp = getNext();
setCoord(ref temp, getCoord(getCurr(), false) + n);




// Setting new score and highscore
void CalculateScore(int value)

Score += value;
if (Score > Highscore)
Highscore = Score;


// Choosing movement direction of corresponding key
void HandleKey()

prevCells = cells.Select(c => (Cell)c.Clone()).ToList();
ConsoleKeyInfo cki = Console.ReadKey(true);
switch (cki.Key)

case ConsoleKey.LeftArrow:
MoveCells(Direction.Left);
break;
case ConsoleKey.RightArrow:
MoveCells(Direction.Right);
break;
case ConsoleKey.UpArrow:
MoveCells(Direction.Up);
break;
case ConsoleKey.DownArrow:
MoveCells(Direction.Down);
break;



// Checking for field overflow
bool IsFieldFull()

return cells.Count() == Length * Width;


// Checking if there is no cell that can be moved
bool IsOver()

if (!IsFieldFull())
return false;

cells = cells.OrderBy(c => c.X).ThenBy(c => c.Y).ToList();

int[,] values = new int[Length, Width];

foreach (Cell cell in cells)

values[cell.X - 1, cell.Y - 1] = cell.Value;


for (int i = 0; i < Length; i++)

for (int j = 0; j < Width; j++)
center == bottom)
return false;


return true;


// Searching for cell with 2048
bool IsWon()

return cells.Any(c => c.Value == 2048);



// Class for changing color for cells
static class ColorChanger

// Get color for corresponding value of cell
static ConsoleColor GetCellColor(string value)

switch (value)

case "2":
return ConsoleColor.Blue;
case "4":
return ConsoleColor.Magenta;
case "8":
return ConsoleColor.Cyan;
case "16":
return ConsoleColor.Green;
case "32":
return ConsoleColor.Yellow;
case "64":
return ConsoleColor.DarkBlue;
case "128":
return ConsoleColor.DarkMagenta;
case "256":
return ConsoleColor.DarkCyan;
case "512":
return ConsoleColor.DarkGreen;
case "1024":
return ConsoleColor.DarkYellow;
default:
return ConsoleColor.Red;



// Color certain cell with optional background color
public static void ColorCell(string value, ConsoleColor bgColor = ConsoleColor.Black)

ConsoleColor defaultFg = Console.ForegroundColor;
ConsoleColor defaultBg = Console.BackgroundColor;

Console.ForegroundColor = GetCellColor(value);
Console.BackgroundColor = bgColor;

Console.WriteLine(value);

Console.ForegroundColor = defaultFg;
Console.BackgroundColor = defaultBg;









share|improve this question

















  • 2




    Bravo, well done! The code is very readable and well structured.
    – Olivier Jacot-Descombes
    Feb 24 at 15:26










  • @OlivierJacot-Descombes, thanks, but aside of that I still think there is a room for improvement :)
    – Glitch
    Feb 24 at 15:33











  • The code is very readable and well structured. you think? I'm not so sure about it...
    – t3chb0t
    Feb 25 at 15:30










  • @t3chb0t then provide some clarification, what you think is wrong and needs to be improved?
    – Glitch
    Feb 25 at 15:42












up vote
5
down vote

favorite
3









up vote
5
down vote

favorite
3






3





So about a week ago I decided to write a simple console version of 2048 game. Well, as you'll see, it came out not that simple... And took a lot more time and practice than expected (should've done it with 2d array...). But anyway here's a code, and I want to know how can it be optimized and improved? I want to know my mistakes and how it can be done easier. If you have some general tips on coding style, readability or anything else I'd appreciate your help.



using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace Game

class Program

static void Main()

Console.CursorVisible = false;

do

Game game = new Game(4, 8, 2);

string key;
do

Console.WriteLine("1. New game");
Console.WriteLine("2. Quit");
key = Console.ReadLine();
Console.Clear();
while (key != "1" && key != "2");

switch (key)

case "1":
game.Run();
break;
case "2":
Environment.Exit(0);
break;

Console.Clear();
while (true);



public enum Direction

Left,
Right,
Up,
Down


class Cell : IEquatable<Cell>, ICloneable

public int Value get; set;
public int X get; set;
public int Y get; set;

public Cell(int value, int x, int y)

Value = value;
X = x;
Y = y;


// Double the value
public void Double() => Value *= 2;

// Display value of cell at certain coordinates
public void Display()

Console.SetCursorPosition((Y - 1) * 5 + 1, (X - 1) * 2 + 1);
ColorChanger.ColorCell(Value.ToString());


// Erase value of cell at certain coordinates
public void Erase()

Console.SetCursorPosition((Y - 1) * 5 + 1, (X - 1) * 2 + 1);
ColorChanger.ColorCell(" ");


#region overriding section

public static bool operator ==(Cell cell1, Cell cell2)

return cell1.Value == cell2.Value && cell1.X == cell2.X && cell1.Y == cell2.Y;


public static bool operator !=(Cell cell1, Cell cell2)

return !(cell1.Value == cell2.Value && cell1.X == cell2.X && cell1.Y == cell2.Y);


public bool Equals(Cell cell)

if (cell is null)
return false;

return Value == cell.Value && X == cell.X && Y == cell.Y;


public override bool Equals(object obj)

Cell cell = obj as Cell;
if (obj == null)
return false;
return Value == cell.Value && X == cell.X && Y == cell.Y;


public override int GetHashCode()

return Value.GetHashCode() ^ X.GetHashCode() ^ Y.GetHashCode();


public object Clone() => MemberwiseClone();

#endregion


class Game

// Length & Width of game field (doesn't have to be equal)
int length;
int width;

int Length

get => length;
set

if (value >= 2)
length = value;
else
length = 2;


int Width

get => width;
set

if (value >= 2)
width = value;
else
width = 2;



// Stats of current game
int Score get; set;
static int Highscore get; set;
int Moves get; set;

// List of current cells & cells on previous move
List<Cell> cells;
List<Cell> prevCells;

Random rand = new Random();

// Initializing the game with field parameters and cell amount at the beginning
public Game(int length, int width, int initialCellAmount)

cells = new List<Cell>();
prevCells = new List<Cell>();
Length = length;
Width = width;
Score = 0;
Moves = 0;

for (int i = 0; i < initialCellAmount; i++)
AddCell();


// Launching the game
public void Run()

DisplayField();
DisplayCells();
DisplayStats();

do

if (Console.KeyAvailable)

while (true);


// Checking for any cell on field that moved
bool IsMoved()

foreach (Cell cell in prevCells)

if (cells.Contains(cell) == false)

Moves++;
return true;


return false;


// Adding new cell on the field (90% - '2', 10% - '4')
void AddCell()

if (IsFieldFull())
return;
Cell cell;
do

cell = new Cell(rand.NextDouble() < 0.9 ? 2 : 4, rand.Next(1, Length), rand.Next(1, Width));
while (cells.Any(c => c.X == cell.X && c.Y == cell.Y));
cells.Add(cell);


// Drawing the field
void DisplayField()

for (int i = 0; i < Length + 1; i++)
");

Console.WriteLine();



// Displaying existing cells
void DisplayCells()

foreach (Cell cell in cells)
cell.Display();


// Displaying score, highscore and moves
void DisplayStats()

Console.SetCursorPosition(0, 9);
Console.WriteLine($"Score: Score Highscore: Highscoren" +
$"Moves: Moves");


// Cells movement algorithm
void MoveCells(Direction dir)
dir == Direction.Up ? 1 : -1;

for (int i = -1; i + 1 < cells.Count(); i++)
getCoord(getCurr(), true) != getCoord(getNext(), true))

Cell temp = getNext();
setCoord(ref temp, border);
continue;


if (getCurr().Value == getNext().Value)

getCurr().Double();
CalculateScore(getCurr().Value);
cells.Remove(getNext());
i--;

else

Cell temp = getNext();
setCoord(ref temp, getCoord(getCurr(), false) + n);




// Setting new score and highscore
void CalculateScore(int value)

Score += value;
if (Score > Highscore)
Highscore = Score;


// Choosing movement direction of corresponding key
void HandleKey()

prevCells = cells.Select(c => (Cell)c.Clone()).ToList();
ConsoleKeyInfo cki = Console.ReadKey(true);
switch (cki.Key)

case ConsoleKey.LeftArrow:
MoveCells(Direction.Left);
break;
case ConsoleKey.RightArrow:
MoveCells(Direction.Right);
break;
case ConsoleKey.UpArrow:
MoveCells(Direction.Up);
break;
case ConsoleKey.DownArrow:
MoveCells(Direction.Down);
break;



// Checking for field overflow
bool IsFieldFull()

return cells.Count() == Length * Width;


// Checking if there is no cell that can be moved
bool IsOver()

if (!IsFieldFull())
return false;

cells = cells.OrderBy(c => c.X).ThenBy(c => c.Y).ToList();

int[,] values = new int[Length, Width];

foreach (Cell cell in cells)

values[cell.X - 1, cell.Y - 1] = cell.Value;


for (int i = 0; i < Length; i++)

for (int j = 0; j < Width; j++)
center == bottom)
return false;


return true;


// Searching for cell with 2048
bool IsWon()

return cells.Any(c => c.Value == 2048);



// Class for changing color for cells
static class ColorChanger

// Get color for corresponding value of cell
static ConsoleColor GetCellColor(string value)

switch (value)

case "2":
return ConsoleColor.Blue;
case "4":
return ConsoleColor.Magenta;
case "8":
return ConsoleColor.Cyan;
case "16":
return ConsoleColor.Green;
case "32":
return ConsoleColor.Yellow;
case "64":
return ConsoleColor.DarkBlue;
case "128":
return ConsoleColor.DarkMagenta;
case "256":
return ConsoleColor.DarkCyan;
case "512":
return ConsoleColor.DarkGreen;
case "1024":
return ConsoleColor.DarkYellow;
default:
return ConsoleColor.Red;



// Color certain cell with optional background color
public static void ColorCell(string value, ConsoleColor bgColor = ConsoleColor.Black)

ConsoleColor defaultFg = Console.ForegroundColor;
ConsoleColor defaultBg = Console.BackgroundColor;

Console.ForegroundColor = GetCellColor(value);
Console.BackgroundColor = bgColor;

Console.WriteLine(value);

Console.ForegroundColor = defaultFg;
Console.BackgroundColor = defaultBg;









share|improve this question













So about a week ago I decided to write a simple console version of 2048 game. Well, as you'll see, it came out not that simple... And took a lot more time and practice than expected (should've done it with 2d array...). But anyway here's a code, and I want to know how can it be optimized and improved? I want to know my mistakes and how it can be done easier. If you have some general tips on coding style, readability or anything else I'd appreciate your help.



using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace Game

class Program

static void Main()

Console.CursorVisible = false;

do

Game game = new Game(4, 8, 2);

string key;
do

Console.WriteLine("1. New game");
Console.WriteLine("2. Quit");
key = Console.ReadLine();
Console.Clear();
while (key != "1" && key != "2");

switch (key)

case "1":
game.Run();
break;
case "2":
Environment.Exit(0);
break;

Console.Clear();
while (true);



public enum Direction

Left,
Right,
Up,
Down


class Cell : IEquatable<Cell>, ICloneable

public int Value get; set;
public int X get; set;
public int Y get; set;

public Cell(int value, int x, int y)

Value = value;
X = x;
Y = y;


// Double the value
public void Double() => Value *= 2;

// Display value of cell at certain coordinates
public void Display()

Console.SetCursorPosition((Y - 1) * 5 + 1, (X - 1) * 2 + 1);
ColorChanger.ColorCell(Value.ToString());


// Erase value of cell at certain coordinates
public void Erase()

Console.SetCursorPosition((Y - 1) * 5 + 1, (X - 1) * 2 + 1);
ColorChanger.ColorCell(" ");


#region overriding section

public static bool operator ==(Cell cell1, Cell cell2)

return cell1.Value == cell2.Value && cell1.X == cell2.X && cell1.Y == cell2.Y;


public static bool operator !=(Cell cell1, Cell cell2)

return !(cell1.Value == cell2.Value && cell1.X == cell2.X && cell1.Y == cell2.Y);


public bool Equals(Cell cell)

if (cell is null)
return false;

return Value == cell.Value && X == cell.X && Y == cell.Y;


public override bool Equals(object obj)

Cell cell = obj as Cell;
if (obj == null)
return false;
return Value == cell.Value && X == cell.X && Y == cell.Y;


public override int GetHashCode()

return Value.GetHashCode() ^ X.GetHashCode() ^ Y.GetHashCode();


public object Clone() => MemberwiseClone();

#endregion


class Game

// Length & Width of game field (doesn't have to be equal)
int length;
int width;

int Length

get => length;
set

if (value >= 2)
length = value;
else
length = 2;


int Width

get => width;
set

if (value >= 2)
width = value;
else
width = 2;



// Stats of current game
int Score get; set;
static int Highscore get; set;
int Moves get; set;

// List of current cells & cells on previous move
List<Cell> cells;
List<Cell> prevCells;

Random rand = new Random();

// Initializing the game with field parameters and cell amount at the beginning
public Game(int length, int width, int initialCellAmount)

cells = new List<Cell>();
prevCells = new List<Cell>();
Length = length;
Width = width;
Score = 0;
Moves = 0;

for (int i = 0; i < initialCellAmount; i++)
AddCell();


// Launching the game
public void Run()

DisplayField();
DisplayCells();
DisplayStats();

do

if (Console.KeyAvailable)

while (true);


// Checking for any cell on field that moved
bool IsMoved()

foreach (Cell cell in prevCells)

if (cells.Contains(cell) == false)

Moves++;
return true;


return false;


// Adding new cell on the field (90% - '2', 10% - '4')
void AddCell()

if (IsFieldFull())
return;
Cell cell;
do

cell = new Cell(rand.NextDouble() < 0.9 ? 2 : 4, rand.Next(1, Length), rand.Next(1, Width));
while (cells.Any(c => c.X == cell.X && c.Y == cell.Y));
cells.Add(cell);


// Drawing the field
void DisplayField()

for (int i = 0; i < Length + 1; i++)
");

Console.WriteLine();



// Displaying existing cells
void DisplayCells()

foreach (Cell cell in cells)
cell.Display();


// Displaying score, highscore and moves
void DisplayStats()

Console.SetCursorPosition(0, 9);
Console.WriteLine($"Score: Score Highscore: Highscoren" +
$"Moves: Moves");


// Cells movement algorithm
void MoveCells(Direction dir)
dir == Direction.Up ? 1 : -1;

for (int i = -1; i + 1 < cells.Count(); i++)
getCoord(getCurr(), true) != getCoord(getNext(), true))

Cell temp = getNext();
setCoord(ref temp, border);
continue;


if (getCurr().Value == getNext().Value)

getCurr().Double();
CalculateScore(getCurr().Value);
cells.Remove(getNext());
i--;

else

Cell temp = getNext();
setCoord(ref temp, getCoord(getCurr(), false) + n);




// Setting new score and highscore
void CalculateScore(int value)

Score += value;
if (Score > Highscore)
Highscore = Score;


// Choosing movement direction of corresponding key
void HandleKey()

prevCells = cells.Select(c => (Cell)c.Clone()).ToList();
ConsoleKeyInfo cki = Console.ReadKey(true);
switch (cki.Key)

case ConsoleKey.LeftArrow:
MoveCells(Direction.Left);
break;
case ConsoleKey.RightArrow:
MoveCells(Direction.Right);
break;
case ConsoleKey.UpArrow:
MoveCells(Direction.Up);
break;
case ConsoleKey.DownArrow:
MoveCells(Direction.Down);
break;



// Checking for field overflow
bool IsFieldFull()

return cells.Count() == Length * Width;


// Checking if there is no cell that can be moved
bool IsOver()

if (!IsFieldFull())
return false;

cells = cells.OrderBy(c => c.X).ThenBy(c => c.Y).ToList();

int[,] values = new int[Length, Width];

foreach (Cell cell in cells)

values[cell.X - 1, cell.Y - 1] = cell.Value;


for (int i = 0; i < Length; i++)

for (int j = 0; j < Width; j++)
center == bottom)
return false;


return true;


// Searching for cell with 2048
bool IsWon()

return cells.Any(c => c.Value == 2048);



// Class for changing color for cells
static class ColorChanger

// Get color for corresponding value of cell
static ConsoleColor GetCellColor(string value)

switch (value)

case "2":
return ConsoleColor.Blue;
case "4":
return ConsoleColor.Magenta;
case "8":
return ConsoleColor.Cyan;
case "16":
return ConsoleColor.Green;
case "32":
return ConsoleColor.Yellow;
case "64":
return ConsoleColor.DarkBlue;
case "128":
return ConsoleColor.DarkMagenta;
case "256":
return ConsoleColor.DarkCyan;
case "512":
return ConsoleColor.DarkGreen;
case "1024":
return ConsoleColor.DarkYellow;
default:
return ConsoleColor.Red;



// Color certain cell with optional background color
public static void ColorCell(string value, ConsoleColor bgColor = ConsoleColor.Black)

ConsoleColor defaultFg = Console.ForegroundColor;
ConsoleColor defaultBg = Console.BackgroundColor;

Console.ForegroundColor = GetCellColor(value);
Console.BackgroundColor = bgColor;

Console.WriteLine(value);

Console.ForegroundColor = defaultFg;
Console.BackgroundColor = defaultBg;











share|improve this question












share|improve this question




share|improve this question








edited Feb 24 at 15:06
























asked Feb 24 at 14:55









Glitch

756




756







  • 2




    Bravo, well done! The code is very readable and well structured.
    – Olivier Jacot-Descombes
    Feb 24 at 15:26










  • @OlivierJacot-Descombes, thanks, but aside of that I still think there is a room for improvement :)
    – Glitch
    Feb 24 at 15:33











  • The code is very readable and well structured. you think? I'm not so sure about it...
    – t3chb0t
    Feb 25 at 15:30










  • @t3chb0t then provide some clarification, what you think is wrong and needs to be improved?
    – Glitch
    Feb 25 at 15:42












  • 2




    Bravo, well done! The code is very readable and well structured.
    – Olivier Jacot-Descombes
    Feb 24 at 15:26










  • @OlivierJacot-Descombes, thanks, but aside of that I still think there is a room for improvement :)
    – Glitch
    Feb 24 at 15:33











  • The code is very readable and well structured. you think? I'm not so sure about it...
    – t3chb0t
    Feb 25 at 15:30










  • @t3chb0t then provide some clarification, what you think is wrong and needs to be improved?
    – Glitch
    Feb 25 at 15:42







2




2




Bravo, well done! The code is very readable and well structured.
– Olivier Jacot-Descombes
Feb 24 at 15:26




Bravo, well done! The code is very readable and well structured.
– Olivier Jacot-Descombes
Feb 24 at 15:26












@OlivierJacot-Descombes, thanks, but aside of that I still think there is a room for improvement :)
– Glitch
Feb 24 at 15:33





@OlivierJacot-Descombes, thanks, but aside of that I still think there is a room for improvement :)
– Glitch
Feb 24 at 15:33













The code is very readable and well structured. you think? I'm not so sure about it...
– t3chb0t
Feb 25 at 15:30




The code is very readable and well structured. you think? I'm not so sure about it...
– t3chb0t
Feb 25 at 15:30












@t3chb0t then provide some clarification, what you think is wrong and needs to be improved?
– Glitch
Feb 25 at 15:42




@t3chb0t then provide some clarification, what you think is wrong and needs to be improved?
– Glitch
Feb 25 at 15:42










1 Answer
1






active

oldest

votes

















up vote
4
down vote



accepted










Bravo, well done! The code is very readable and well structured.



If you intend to make several console games, you will notice that some things remain the same. To allow reusability I suggest abstracting certain aspects through interfaces. It also reduces dependencies between components, enhances testability and allows you to easily replace a component by another one.



public interface IMainUserInterface

void StartGame(IGameLoop gameLoop, IGame game);


public interface IGameLoop

void Run(IGame game);


public interface IGame

void DisplayField();
void DisplayCells();
void DisplayStats();
void MakeMove(ConsoleKeyInfo cki);
bool IsOver();
bool IsWon();



The IMainUserInterface implementation:



public class MainUserInterface : IMainUserInterface

public void StartGame(IGameLoop gameLoop, IGame game)

Console.CursorVisible = false;
do

string key;
do

Console.WriteLine("1. New game");
Console.WriteLine("2. Quit");
key = Console.ReadLine();
Console.Clear();
while (key != "1" && key != "2");
switch (key)

case "1":
gameLoop.Run(game);
break;
case "2":
Environment.Exit(0);
break;

Console.Clear();
while (true);




The IGameLoop implementation:



public class GameLoop : IGameLoop

void Run(IGame game)

game.DisplayField();
game.DisplayCells();
game.DisplayStats();

do

if (Console.KeyAvailable)

game.MakeMove(Console.ReadKey(true));
game.DisplayCells();
game.DisplayStats();

bool isOver = game.IsOver();
bool isWon = game.IsWon();

if (isOver
while (true);




I won't show the game implementation here. The Main method becomes



class Program

static void Main()

IMainUserInterface userInterface = new MainUserInterface();
IGameLoop gameLoop = new GameLoop();
IGame game = new Game2048();

userInterface.StartGame(gameLoop, game);







share|improve this answer























  • Thank you, I will definitely include interfaces in my next projects :)
    – Glitch
    Feb 24 at 18:05










Your Answer




StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");

StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);








 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f188273%2f2048-console-game-c%23new-answer', 'question_page');

);

Post as a guest






























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
4
down vote



accepted










Bravo, well done! The code is very readable and well structured.



If you intend to make several console games, you will notice that some things remain the same. To allow reusability I suggest abstracting certain aspects through interfaces. It also reduces dependencies between components, enhances testability and allows you to easily replace a component by another one.



public interface IMainUserInterface

void StartGame(IGameLoop gameLoop, IGame game);


public interface IGameLoop

void Run(IGame game);


public interface IGame

void DisplayField();
void DisplayCells();
void DisplayStats();
void MakeMove(ConsoleKeyInfo cki);
bool IsOver();
bool IsWon();



The IMainUserInterface implementation:



public class MainUserInterface : IMainUserInterface

public void StartGame(IGameLoop gameLoop, IGame game)

Console.CursorVisible = false;
do

string key;
do

Console.WriteLine("1. New game");
Console.WriteLine("2. Quit");
key = Console.ReadLine();
Console.Clear();
while (key != "1" && key != "2");
switch (key)

case "1":
gameLoop.Run(game);
break;
case "2":
Environment.Exit(0);
break;

Console.Clear();
while (true);




The IGameLoop implementation:



public class GameLoop : IGameLoop

void Run(IGame game)

game.DisplayField();
game.DisplayCells();
game.DisplayStats();

do

if (Console.KeyAvailable)

game.MakeMove(Console.ReadKey(true));
game.DisplayCells();
game.DisplayStats();

bool isOver = game.IsOver();
bool isWon = game.IsWon();

if (isOver
while (true);




I won't show the game implementation here. The Main method becomes



class Program

static void Main()

IMainUserInterface userInterface = new MainUserInterface();
IGameLoop gameLoop = new GameLoop();
IGame game = new Game2048();

userInterface.StartGame(gameLoop, game);







share|improve this answer























  • Thank you, I will definitely include interfaces in my next projects :)
    – Glitch
    Feb 24 at 18:05














up vote
4
down vote



accepted










Bravo, well done! The code is very readable and well structured.



If you intend to make several console games, you will notice that some things remain the same. To allow reusability I suggest abstracting certain aspects through interfaces. It also reduces dependencies between components, enhances testability and allows you to easily replace a component by another one.



public interface IMainUserInterface

void StartGame(IGameLoop gameLoop, IGame game);


public interface IGameLoop

void Run(IGame game);


public interface IGame

void DisplayField();
void DisplayCells();
void DisplayStats();
void MakeMove(ConsoleKeyInfo cki);
bool IsOver();
bool IsWon();



The IMainUserInterface implementation:



public class MainUserInterface : IMainUserInterface

public void StartGame(IGameLoop gameLoop, IGame game)

Console.CursorVisible = false;
do

string key;
do

Console.WriteLine("1. New game");
Console.WriteLine("2. Quit");
key = Console.ReadLine();
Console.Clear();
while (key != "1" && key != "2");
switch (key)

case "1":
gameLoop.Run(game);
break;
case "2":
Environment.Exit(0);
break;

Console.Clear();
while (true);




The IGameLoop implementation:



public class GameLoop : IGameLoop

void Run(IGame game)

game.DisplayField();
game.DisplayCells();
game.DisplayStats();

do

if (Console.KeyAvailable)

game.MakeMove(Console.ReadKey(true));
game.DisplayCells();
game.DisplayStats();

bool isOver = game.IsOver();
bool isWon = game.IsWon();

if (isOver
while (true);




I won't show the game implementation here. The Main method becomes



class Program

static void Main()

IMainUserInterface userInterface = new MainUserInterface();
IGameLoop gameLoop = new GameLoop();
IGame game = new Game2048();

userInterface.StartGame(gameLoop, game);







share|improve this answer























  • Thank you, I will definitely include interfaces in my next projects :)
    – Glitch
    Feb 24 at 18:05












up vote
4
down vote



accepted







up vote
4
down vote



accepted






Bravo, well done! The code is very readable and well structured.



If you intend to make several console games, you will notice that some things remain the same. To allow reusability I suggest abstracting certain aspects through interfaces. It also reduces dependencies between components, enhances testability and allows you to easily replace a component by another one.



public interface IMainUserInterface

void StartGame(IGameLoop gameLoop, IGame game);


public interface IGameLoop

void Run(IGame game);


public interface IGame

void DisplayField();
void DisplayCells();
void DisplayStats();
void MakeMove(ConsoleKeyInfo cki);
bool IsOver();
bool IsWon();



The IMainUserInterface implementation:



public class MainUserInterface : IMainUserInterface

public void StartGame(IGameLoop gameLoop, IGame game)

Console.CursorVisible = false;
do

string key;
do

Console.WriteLine("1. New game");
Console.WriteLine("2. Quit");
key = Console.ReadLine();
Console.Clear();
while (key != "1" && key != "2");
switch (key)

case "1":
gameLoop.Run(game);
break;
case "2":
Environment.Exit(0);
break;

Console.Clear();
while (true);




The IGameLoop implementation:



public class GameLoop : IGameLoop

void Run(IGame game)

game.DisplayField();
game.DisplayCells();
game.DisplayStats();

do

if (Console.KeyAvailable)

game.MakeMove(Console.ReadKey(true));
game.DisplayCells();
game.DisplayStats();

bool isOver = game.IsOver();
bool isWon = game.IsWon();

if (isOver
while (true);




I won't show the game implementation here. The Main method becomes



class Program

static void Main()

IMainUserInterface userInterface = new MainUserInterface();
IGameLoop gameLoop = new GameLoop();
IGame game = new Game2048();

userInterface.StartGame(gameLoop, game);







share|improve this answer















Bravo, well done! The code is very readable and well structured.



If you intend to make several console games, you will notice that some things remain the same. To allow reusability I suggest abstracting certain aspects through interfaces. It also reduces dependencies between components, enhances testability and allows you to easily replace a component by another one.



public interface IMainUserInterface

void StartGame(IGameLoop gameLoop, IGame game);


public interface IGameLoop

void Run(IGame game);


public interface IGame

void DisplayField();
void DisplayCells();
void DisplayStats();
void MakeMove(ConsoleKeyInfo cki);
bool IsOver();
bool IsWon();



The IMainUserInterface implementation:



public class MainUserInterface : IMainUserInterface

public void StartGame(IGameLoop gameLoop, IGame game)

Console.CursorVisible = false;
do

string key;
do

Console.WriteLine("1. New game");
Console.WriteLine("2. Quit");
key = Console.ReadLine();
Console.Clear();
while (key != "1" && key != "2");
switch (key)

case "1":
gameLoop.Run(game);
break;
case "2":
Environment.Exit(0);
break;

Console.Clear();
while (true);




The IGameLoop implementation:



public class GameLoop : IGameLoop

void Run(IGame game)

game.DisplayField();
game.DisplayCells();
game.DisplayStats();

do

if (Console.KeyAvailable)

game.MakeMove(Console.ReadKey(true));
game.DisplayCells();
game.DisplayStats();

bool isOver = game.IsOver();
bool isWon = game.IsWon();

if (isOver
while (true);




I won't show the game implementation here. The Main method becomes



class Program

static void Main()

IMainUserInterface userInterface = new MainUserInterface();
IGameLoop gameLoop = new GameLoop();
IGame game = new Game2048();

userInterface.StartGame(gameLoop, game);








share|improve this answer















share|improve this answer



share|improve this answer








edited Mar 8 at 0:21


























answered Feb 24 at 16:09









Olivier Jacot-Descombes

2,3611016




2,3611016











  • Thank you, I will definitely include interfaces in my next projects :)
    – Glitch
    Feb 24 at 18:05
















  • Thank you, I will definitely include interfaces in my next projects :)
    – Glitch
    Feb 24 at 18:05















Thank you, I will definitely include interfaces in my next projects :)
– Glitch
Feb 24 at 18:05




Thank you, I will definitely include interfaces in my next projects :)
– Glitch
Feb 24 at 18:05












 

draft saved


draft discarded


























 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f188273%2f2048-console-game-c%23new-answer', 'question_page');

);

Post as a guest













































































Popular posts from this blog

Greedy Best First Search implementation in Rust

Function to Return a JSON Like Objects Using VBA Collections and Arrays

C++11 CLH Lock Implementation