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); } } } } 
  • I also use it and stack - user275180

2 answers 2

but it is canceled only once

This is because calling the Move method inside the Undo method results in writing this action into the turn history. Add a new parameter to the Move method with a default value corresponding to the current behavior. From Undo , call Move with this parameter so that a move recording is not performed.

    There is a normal solution, you can record all changes in the log, and then from that log take the necessary information. You can implement it via Stack() , or you can use serialization to some file and then read from there.