1, Effect display
2, Play instructions
the rules of the game are very simple. You need to control all squares to move in the same direction. After two identical digital squares collide together, they are merged into their sum. After each operation, a 2 or 4 will be randomly generated, and finally a "2048" square will be regarded as victory.
3, Main logic
- 4 * 4 two-dimensional array, then traverse the elements inside, count all zeros, put them in a list, and assign Random.next(0,list.count) to 2
- The loop begins, initializing the command line
- Enter the function corresponding to the four operations up, down, left and right on the keyboard. After the operation, first traverse whether there are spaces. If there are spaces, continue. If there are no spaces, you have to judge whether you can move again. If not, you will die
- Just write the logic to the left and down, and take the opposite to the right and up
- If any element is 2048, it wins
4, Code implementation
4.1 game start
Game start:
- Randomly generate two 2
- Start game cycle
- Each verification game is enough to end
- Detect user operation (up, down, left and right)
- Output current status to console
/// <summary> ///The game begins /// </summary> public void GameStart() { Add2(); Add2(); Output(); while (true) { // It is used to traverse the bool type variable used to detect whether there is a difference between after pressing the key and before bool flag = false; // Victory condition traversal foreach (int item in arr) { if (item == 2048) { Console.WriteLine("\n(ノ´▽`)ノ♪ ------ Game victory ------ (ノ´▽`)ノ♪"); Last(); } } // This is a backup array used to detect whether there is a difference between after pressing the key and before int[,] arrtmp = new int[4, 4]; // Traversal assigns a value to the backup array for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { arrtmp[i, j] = arr[i, j]; } } // Get user actions -- > up, down, left and right ConsoleKeyInfo info = Console.ReadKey(true); switch (info.Key) { case ConsoleKey.UpArrow: MoveUp(); break; case ConsoleKey.DownArrow: MoveDown(); break; case ConsoleKey.LeftArrow: MoveLeft(); break; case ConsoleKey.RightArrow: MoveRight(); break; } // Traverse to detect whether the state before pressing the direction key is exactly the same as that after pressing the direction key for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (arrtmp[i, j] != arr[i, j]) { // Once any element is different before and after, false is changed to true flag = true; } } } if (flag) { // If false is true, it means that it has changed. If it has changed, brush a 2, // On the contrary, do nothing Add2(); } // Output to console Output(); // Detect whether it is dead after pressing the direction key if (!End()) { Console.WriteLine("\n(;´д`)ゞ ------ Game failure ------ (;´д`)ゞ"); Last(); } } }
4.2 random generation
Traverse non-zero elements and randomly assign one to 2
public void Add2() { listOfCoo.Clear(); // Traverse the coordinates of all zero elements for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (arr[i, j] == 0) { // Instantiate the traversed coordinates as parameters CoordinateTools coo = new CoordinateTools(i, j); // add the instantiation result to the list listOfCoo.Add(coo); } } } // If no element in the list is saved, there is no space in the table and you can exit directly if (listOfCoo.Count == 0) { return; } // Take a random position from the table int cooPlus = rd.Next(0, listOfCoo.Count); // Change this position assignment to 2 arr[listOfCoo[cooPlus].x, listOfCoo[cooPlus].y] = 2; }
4.3 digital mobile
Moved down as an example:
- Non-zero numbers move downward. When non-zero numbers are encountered, they are accumulated if they are the same, and saved to the current position if they are different;
- If you move up, just turn it over again
public void MoveDown() { for (int j = 0; j < 4; j++) { for (int i = 2; i >= 0; i--) { if (arr[i, j] == 0) continue; for (int k = i + 1; k < 4; k++) { if (arr[k, j] != 0) { if (arr[i, j] == arr[k, j]) { arr[k, j] += arr[i, j]; arr[i, j] = 0; break; } else if (arr[i, j] != arr[k, j] && k - 1 != i) { arr[k - 1, j] = arr[i, j]; arr[i, j] = 0; break; } else if (arr[i, j] != arr[k, j] && k - 1 == i) { break; } } if (k == 3) { arr[k, j] = arr[i, j]; arr[i, j] = 0; break; } } } } }
4.4 judging the outcome
Judge the end of the game:
- Any empty element in the traversal array indicates that it cannot die
- Traverse from 2 to 2048; The purpose is to detect whether each number is adjacent up, down, left and right, and whether there is the same number as him
// Judge the end of the game public bool End() { // Any empty element in the traversal array indicates that it cannot die foreach (int item in arr) { if (item == 0) return true; } // Traverse from 2 to 2048 // The purpose is to detect whether each number is adjacent up, down, left and right, and whether there is the same number as him for (int num = 2; num <= 2048; num *= 2) { List<CoordinateTools> listOfget2 = new List<CoordinateTools>(); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (arr[i, j] == num) { // First save the subscripts of all elements with NUM value in the list CoordinateTools coo = new CoordinateTools(i, j); listOfget2.Add(coo); } } } // If the list is empty, it means that there is no num in the current table. Go back to FOR to continue if (listOfget2 == null) { continue; } // Start with the first element in the list (each element contains a set of subscripts x,y) foreach (CoordinateTools item in listOfget2) { foreach (CoordinateTools item2 in listOfget2) { // Judge whether the absolute value of column coordinate difference in the same row is 1 and whether the absolute value of row coordinate difference in the same column is 1 if ((item.y == item2.y && Math.Abs(item.x - item2.x) == 1) || (item.x == item2.x && Math.Abs(item.y - item2.y) == 1)) { // If there's one, you don't have to recycle it. It's definitely not dead return true; } } } } // After the full traversal, it indicates that it is dead and returns false return false; }
4.5 restart
After judging the end of the game, prompt the user whether to "exit" or "play again", and carry out logical processing according to the user's choice
/// <summary> ///Choice after victory or defeat /// </summary> public void Last() { Console.WriteLine("\n input X Exit input R restart\n"); while (true) { string str = Console.ReadLine(); if (str == "x") { Environment.Exit(0); } //Restart -- > initialization if (str == "r") { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { arr[i, j] = 0; } } GameStart(); } } }
5, Source sharing
using System; using System.Collections.Generic; namespace CSharp_2048 { class Program { static void Main(string[] args) { Class2048 class2048 = new Class2048(); class2048.GameStart(); } /// <summary> ///Games 2048 /// </summary> class Class2048 { public int[,] arr = new int[4, 4]; public Random rd = new Random(); public List<CoordinateTools> listOfCoo = new List<CoordinateTools>(); /// <summary> ///Output current status /// </summary> public void Output() { string str = " "; Console.Clear(); Console.WriteLine("┏┉┉┉┉┉┉┉┉┳┉┉┉┉┉┉┉┉┳┉┉┉┉┉┉┉┉┳┉┉┉┉┉┉┉┉┓"); Console.WriteLine("┋ ┋ ┋ ┋ ┋"); Console.WriteLine("┋ {0} ┋ {1} ┋ {2} ┋ {3} ┋", arr[0, 0] == 0 ? str : arr[0, 0].ToString().PadLeft(4, ' '), arr[0, 1] == 0 ? str : arr[0, 1].ToString().PadLeft(4, ' '), arr[0, 2] == 0 ? str : arr[0, 2].ToString().PadLeft(4, ' '), arr[0, 3] == 0 ? str : arr[0, 3].ToString().PadLeft(4, ' ')); Console.WriteLine("┋ ┋ ┋ ┋ ┋"); Console.WriteLine("┣┉┉┉┉┉┉┉┉╋┉┉┉┉┉┉┉┉╋┉┉┉┉┉┉┉┉╋┉┉┉┉┉┉┉┉┫"); Console.WriteLine("┋ ┋ ┋ ┋ ┋"); Console.WriteLine("┋ {0} ┋ {1} ┋ {2} ┋ {3} ┋", arr[1, 0] == 0 ? str : arr[1, 0].ToString().PadLeft(4, ' '), arr[1, 1] == 0 ? str : arr[1, 1].ToString().PadLeft(4, ' '), arr[1, 2] == 0 ? str : arr[1, 2].ToString().PadLeft(4, ' '), arr[1, 3] == 0 ? str : arr[1, 3].ToString().PadLeft(4, ' ')); Console.WriteLine("┋ ┋ ┋ ┋ ┋"); Console.WriteLine("┣┉┉┉┉┉┉┉┉╋┉┉┉┉┉┉┉┉╋┉┉┉┉┉┉┉┉╋┉┉┉┉┉┉┉┉┫"); Console.WriteLine("┋ ┋ ┋ ┋ ┋"); Console.WriteLine("┋ {0} ┋ {1} ┋ {2} ┋ {3} ┋", arr[2, 0] == 0 ? str : arr[2, 0].ToString().PadLeft(4, ' '), arr[2, 1] == 0 ? str : arr[2, 1].ToString().PadLeft(4, ' '), arr[2, 2] == 0 ? str : arr[2, 2].ToString().PadLeft(4, ' '), arr[2, 3] == 0 ? str : arr[2, 3].ToString().PadLeft(4, ' ')); Console.WriteLine("┋ ┋ ┋ ┋ ┋"); Console.WriteLine("┣┉┉┉┉┉┉┉┉╋┉┉┉┉┉┉┉┉╋┉┉┉┉┉┉┉┉╋┉┉┉┉┉┉┉┉┫"); Console.WriteLine("┋ ┋ ┋ ┋ ┋"); Console.WriteLine("┋ {0} ┋ {1} ┋ {2} ┋ {3} ┋", arr[3, 0] == 0 ? str : arr[3, 0].ToString().PadLeft(4, ' '), arr[3, 1] == 0 ? str : arr[3, 1].ToString().PadLeft(4, ' '), arr[3, 2] == 0 ? str : arr[3, 2].ToString().PadLeft(4, ' '), arr[3, 3] == 0 ? str : arr[3, 3].ToString().PadLeft(4, ' ')); Console.WriteLine("┋ ┋ ┋ ┋ ┋"); Console.WriteLine("┗┉┉┉┉┉┉┉┉┻┉┉┉┉┉┉┉┉┻┉┉┉┉┉┉┉┉┻┉┉┉┉┉┉┉┉┛"); Console.WriteLine("\n<<Command line version 2048>> Please press up, down, left and right(↑←↓→)Direction key operation"); } /// <summary> ///Traverse non-zero elements and randomly assign one to 2 /// </summary> public void Add2() { listOfCoo.Clear(); // Traverse the coordinates of all zero elements for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (arr[i, j] == 0) { // Instantiate the traversed coordinates as parameters CoordinateTools coo = new CoordinateTools(i, j); // add the instantiation result to the list listOfCoo.Add(coo); } } } // If no element in the list is saved, there is no space in the table and you can exit directly if (listOfCoo.Count == 0) { return; } // Take a random position from the table int cooPlus = rd.Next(0, listOfCoo.Count); // Change this position assignment to 2 arr[listOfCoo[cooPlus].x, listOfCoo[cooPlus].y] = 2; } /// <summary> ///The game begins /// </summary> public void GameStart() { Add2(); Add2(); Output(); while (true) { // It is used to traverse the bool type variable used to detect whether there is a difference between after pressing the key and before bool flag = false; // Victory condition traversal foreach (int item in arr) { if (item == 2048) { Console.WriteLine("\n(ノ´▽`)ノ♪ ------ Game victory ------ (ノ´▽`)ノ♪"); Last(); } } // This is a backup array used to detect whether there is a difference between after pressing the key and before int[,] arrtmp = new int[4, 4]; // Traversal assigns a value to the backup array for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { arrtmp[i, j] = arr[i, j]; } } // Get user actions -- > up, down, left and right ConsoleKeyInfo info = Console.ReadKey(true); switch (info.Key) { case ConsoleKey.UpArrow: MoveUp(); break; case ConsoleKey.DownArrow: MoveDown(); break; case ConsoleKey.LeftArrow: MoveLeft(); break; case ConsoleKey.RightArrow: MoveRight(); break; } // Traverse to detect whether the state before pressing the direction key is exactly the same as that after pressing the direction key for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (arrtmp[i, j] != arr[i, j]) { // Once any element is different before and after, false is changed to true flag = true; } } } if (flag) { // If false is true, it means that it has changed. If it has changed, brush a 2, // On the contrary, do nothing Add2(); } // Output to console Output(); // Detect whether it is dead after pressing the direction key if (!End()) { Console.WriteLine("\n(;´д`)ゞ ------ Game failure ------ (;´д`)ゞ"); Last(); } } } #region core logic -- > control of moving in four directions /// <summary> ///Move down non-zero numbers. When non-zero numbers are encountered, they are accumulated if they are the same, and saved to the current position if they are different /// </summary> public void MoveDown() { for (int j = 0; j < 4; j++) { for (int i = 2; i >= 0; i--) { if (arr[i, j] == 0) continue; for (int k = i + 1; k < 4; k++) { if (arr[k, j] != 0) { if (arr[i, j] == arr[k, j]) { arr[k, j] += arr[i, j]; arr[i, j] = 0; break; } else if (arr[i, j] != arr[k, j] && k - 1 != i) { arr[k - 1, j] = arr[i, j]; arr[i, j] = 0; break; } else if (arr[i, j] != arr[k, j] && k - 1 == i) { break; } } if (k == 3) { arr[k, j] = arr[i, j]; arr[i, j] = 0; break; } } } } } /// <summary> ///Move up: first flip the array up and down, then move it down, and then flip it back /// </summary> public void MoveUp() { for (int i = 0; i < 2; i++) { for (int j = 0; j < 4; j++) { int tmp = 0; tmp = arr[i, j]; arr[i, j] = arr[3 - i, j]; arr[3 - i, j] = tmp; } } MoveDown(); for (int i = 0; i < 2; i++) { for (int j = 0; j < 4; j++) { int tmp = 0; tmp = arr[i, j]; arr[i, j] = arr[3 - i, j]; arr[3 - i, j] = tmp; } } } /// <summary> ///Move left /// </summary> public void MoveLeft() { for (int i = 0; i < 4; i++) { for (int j = 1; j < 4; j++) { if (arr[i, j] == 0) continue; for (int k = j - 1; k >= 0; k--) { if (arr[i, k] != 0) { if (arr[i, j] == arr[i, k]) { arr[i, k] += arr[i, j]; arr[i, j] = 0; break; } else if (arr[i, j] != arr[i, k] && k + 1 != j) { arr[i, k + 1] = arr[i, j]; arr[i, j] = 0; break; } else if (arr[i, j] != arr[i, k] && k + 1 == j) { break; } } if (k == 0) { arr[i, k] = arr[i, j]; arr[i, j] = 0; break; } } } } } /// <summary> ///Move right: first flip the array left and right, then move it left, and then flip it back /// </summary> public void MoveRight() { for (int j = 0; j < 2; j++) { for (int i = 0; i < 4; i++) { int tmp = 0; tmp = arr[i, j]; arr[i, j] = arr[i, 3 - j]; arr[i, 3 - j] = tmp; } } MoveLeft(); for (int j = 0; j < 2; j++) { for (int i = 0; i < 4; i++) { int tmp = 0; tmp = arr[i, j]; arr[i, j] = arr[i, 3 - j]; arr[i, 3 - j] = tmp; } } } #endregion /// <summary> ///Judge whether it fails /// </summary> /// <returns></returns> public bool End() { // Any empty element in the traversal array indicates that it cannot die foreach (int item in arr) { if (item == 0) return true; } // Traverse from 2 to 2048 // The purpose is to detect whether each number is adjacent up, down, left and right, and whether there is the same number as him for (int num = 2; num <= 2048; num *= 2) { List<CoordinateTools> listOfget2 = new List<CoordinateTools>(); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (arr[i, j] == num) { // First save the subscripts of all elements with NUM value in the list CoordinateTools coo = new CoordinateTools(i, j); listOfget2.Add(coo); } } } // If the list is empty, it means that there is no num in the current table. Go back to FOR to continue if (listOfget2 == null) { continue; } // Start with the first element in the list (each element contains a set of subscripts x,y) foreach (CoordinateTools item in listOfget2) { foreach (CoordinateTools item2 in listOfget2) { // Judge whether the absolute value of column coordinate difference in the same row is 1 and whether the absolute value of row coordinate difference in the same column is 1 if ((item.y == item2.y && Math.Abs(item.x - item2.x) == 1) || (item.x == item2.x && Math.Abs(item.y - item2.y) == 1)) { // If there's one, you don't have to recycle it. It's definitely not dead return true; } } } } // After the full traversal, it indicates that it is dead and returns false return false; } /// <summary> ///Choice after victory or defeat /// </summary> public void Last() { Console.WriteLine("\n input X Exit input R restart\n"); while (true) { string str = Console.ReadLine(); if (str == "x") { Environment.Exit(0); } //Restart -- > initialization if (str == "r") { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { arr[i, j] = 0; } } GameStart(); } } } } /// <summary> ///The tool class is used to store the index of the searched array /// </summary> class CoordinateTools { public int x { set; get; } public int y { set; get; } public CoordinateTools(int i, int j) { this.x = i; this.y = j; } } } }