It is necessary that when calling the function undo (Game3) the move is canceled, it is canceled, but it is canceled only once how to do what needs to be changed so that you can do a lot of rollback
//Program.cs using System; namespace Pyatnyasky { class Program { static void Main(string[] args) { int score = 0; int a; int[] p = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0 }; Console.WriteLine("15 Puzle"); Console.WriteLine("-1 - cancel the match \n 0 - cancel the difference \n 16 - exit"); Game2 Random = new Game2(p); Random.mixer(p); Game3 MyGame = new Game3(p); MyGame.drawField(); for (;;) { Console.WriteLine("Select number: "); string input = Console.ReadLine(); if (Int32.TryParse(input, out a)) { if (a == -1) { MyGame.Undo(); MyGame.drawField(); } else if (a == 0) { MyGame.Redo(); MyGame.drawField(); } else if(a == 16) { break; } else { MyGame.Move(a, MyGame); MyGame.drawField(); score++; Console.WriteLine("Number of moves: " + score); } } else { Console.WriteLine("Input error"); } if (MyGame.finish()) { MyGame.drawField(); Console.WriteLine("Victory!"); Console.WriteLine("End of the game"); break; } } Console.ReadKey(); } } } //Game.cs using System; namespace Pyatnyasky { class Points { public int x, y; public Points(int x, int y) { this.x = x; this.y = y; } } class Game { public int[] point = new int[16]; public int Length = 0; public static int[] ArrayText = new int[16]; public const int width = 4, height = 4; public int[,] field = new int[width, height]; public Points[] FieldValue = new Points[16]; public Game(int[] point) { int r = 0; Length = width * height; for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { field[j, i] = point[r]; FieldValue[point[r]] = new Points(j, i); r++; } } } public int this[int x, int y] { get { if (x < 0 || x >= width * height || y < 0 || y >= width * height) { throw new ArgumentOutOfRangeException("the indexes do not fit"); } return field[x, y]; } } public Points GetLocation(int value) { return FieldValue[value]; } public void drawField() { Console.WriteLine("____________________________________"); for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { Console.Write(field[i, j] + "\t"); } Console.WriteLine(); } Console.WriteLine("____________________________________"); } public void Move(int value, Game3 obj) { try { if (value > 15 || value < 0) { throw new ArgumentException(); } int x = GetLocation(0).x; int y = GetLocation(0).y; int X = GetLocation(value).x; int Y = GetLocation(value).y; if ((X == x && (Y == y - 1 || Y == y + 1)) || (Y == y && (X == x - 1 || X == x + 1))) { field[x, y] = value; field[X, Y] = 0; var vere = FieldValue[0]; FieldValue[0] = FieldValue[value]; FieldValue[value] = vere; obj.History(value); } else { throw new Exception(); } } catch (ArgumentException) { Console.WriteLine("There is no such number: "); } catch (Exception) { Console.WriteLine("Along with this number is not 0: "); } } } } //Game2.cs using System; namespace Pyatnyasky { class Game2 : Game { public Game2(int[] point) : base(point) { } public bool finish() { int value = 1; for (int i = 0; i < width; ++i) { for (int j = 0; j < height; ++j) { if (field[i, j] == value) { ++value; if (value == Length) { value = 0; } } else { return false; } } } return true; } public void mixer(int[] p) { int tmp = 0; Random rnd = new Random(); for (int i = 0; i < p.Length; i++) { bool isExist = false; do { isExist = false; tmp = rnd.Next(0, p.Length); for (int j = 0; j < i; j++) { if (tmp == p[j]) { isExist = true; } } } while (isExist); p[i] = tmp; } } } } //Game3.cs using System; using System.Collections.Generic; namespace Pyatnyasky_v2 { class Game3 : Game2 { Stack<Points> undo = new Stack<Points>(); Stack<Points> redo = new Stack<Points>(); public Game3(int[] point) : base(point) { } public void History(int value) { undo.Push(GetLocation(value)); } public void Undo() { if (undo.Count > 0) { var point = undo.Pop(); this.Move(this[point.x, point.y], this); redo.Push(point); } else throw new Exception("Unable to cancel"); } public void Redo() { if (redo.Count > 0) { var point = redo.Pop(); this.Move(this[point.x, point.y], this); undo.Push(point); } } } }