diff --git a/CustomGame.xaml.cs b/CustomGame.xaml.cs index 1ffb741..922e952 100644 --- a/CustomGame.xaml.cs +++ b/CustomGame.xaml.cs @@ -125,7 +125,7 @@ private void startButton_Click(object sender, RoutedEventArgs e) bool currentTurnSente = turnSelectSente.IsChecked ?? false; GeneratedGame = new ShogiGame(Board, currentTurnSente, ShogiGame.EndingStates.Contains(BoardAnalysis.DetermineGameState(Board, currentTurnSente)), - new(), new(), new(), sentePieceDrops, gotePieceDrops, new(), null); + new(), new(), new(), sentePieceDrops, gotePieceDrops, new(), null, null); Close(); } diff --git a/MainWindow.xaml b/MainWindow.xaml index 64ba837..f9e6933 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -30,6 +30,8 @@ + + diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index 7552957..f942b9d 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -979,6 +979,22 @@ private void NotationSetItem_Click(object sender, RoutedEventArgs e) UpdateGameDisplay(); } + private async void UndoMove_Click(object sender, RoutedEventArgs e) + { + if (game.PreviousGameState is not null + && ((game.CurrentTurnSente && !senteIsComputer) || (!game.CurrentTurnSente && !goteIsComputer))) + { + game = game.PreviousGameState; + if (senteIsComputer || goteIsComputer) + { + // Reverse two moves if the opponent is computer controlled + game = game.PreviousGameState!; + } + UpdateGameDisplay(); + await CheckComputerMove(); + } + } + private void GoteDrop_MouseUp(object sender, MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Left && !game.GameOver) diff --git a/ShogiGame.cs b/ShogiGame.cs index 5ebf33c..5b76702 100644 --- a/ShogiGame.cs +++ b/ShogiGame.cs @@ -77,6 +77,7 @@ public class ShogiGame public List<(string, Point, Point, bool, bool)> Moves { get; } public List JapaneseMoveText { get; } public List WesternMoveText { get; } + public ShogiGame? PreviousGameState { get; private set; } public Dictionary SentePieceDrops { get; } public Dictionary GotePieceDrops { get; } @@ -155,7 +156,7 @@ public ShogiGame(Pieces.Piece?[,] board, bool currentTurnSente, bool gameOver, List<(string, Point, Point, bool, bool)> moves, List japaneseMoveText, List westernMoveText, Dictionary? sentePieceDrops, Dictionary? gotePieceDrops, Dictionary boardCounts, - string? initialState) + string? initialState, ShogiGame? previousGameState) { if (board.GetLength(0) is not 9 and not 5 || board.GetLength(1) is not 9 and not 5) { @@ -198,6 +199,7 @@ public ShogiGame(Pieces.Piece?[,] board, bool currentTurnSente, bool gameOver, BoardCounts = boardCounts; InitialState = initialState ?? ToString(); + PreviousGameState = previousGameState; } /// @@ -216,7 +218,7 @@ public ShogiGame Clone() return new ShogiGame(boardClone, CurrentTurnSente, GameOver, new(Moves), new(JapaneseMoveText), new(WesternMoveText), new Dictionary(SentePieceDrops), - new Dictionary(GotePieceDrops), new(BoardCounts), InitialState); + new Dictionary(GotePieceDrops), new(BoardCounts), InitialState, PreviousGameState?.Clone()); } /// @@ -348,7 +350,7 @@ public bool IsDropPossible(Type dropType, Point destination) /// If a piece can be promoted, should it be? means the user should be prompted. /// /// - /// Whether the move should update the game move text. This should usually be , + /// Whether the move should update the game move text and update . This should usually be , /// but may be set to for performance optimisations in clone games for analysis. /// /// if the move was valid and executed, otherwise @@ -393,11 +395,12 @@ public bool MovePiece(Point source, Point destination, bool forceMove = false, b } } - // Used for generating new move text + // Used for generating new move text and move undoing ShogiGame? oldGame = null; if (updateMoveText) { oldGame = Clone(); + PreviousGameState = oldGame; } bool pieceMoved = piece.Move(Board, destination, forceMove || source.X == -1); @@ -976,7 +979,7 @@ public static ShogiGame FromShogiForsythEdwards(string forsythEdwards) // Shogi Forsyth–Edwards doesn't define what the previous moves were, so they moves list starts empty return new ShogiGame(board, currentTurnSente, EndingStates.Contains(BoardAnalysis.DetermineGameState(board, currentTurnSente)), - new(), new(), new(), sentePieceDrops, gotePieceDrops, new(), null); + new(), new(), new(), sentePieceDrops, gotePieceDrops, new(), null, null); } } }