Generally, you need to keep a representation of a starting state,
like the initial configuration of a chess board for example,
and a history stack (list) of the moves that have been made in the game.
For a chess game, each move would name the piece (or starting position)
and the destination.
So that implies a list of lists for the stack, since any move typically needs
multiple pieces of data to describe it.
This technique needs a routine that can accept a list of moves and a starting
state, and generate a current state from that.
To Undo, remove the latest (last) move from the move history list and rebuild
a new current game board from the original board and the shorter move list.
An alternative technique would be to just store each successive board state
on the history stack. To Undo, just remove the last entry, and use the new last entry
as the current board. (This precludes you from being able to show a game log.)
If you want to add a Redo function, I recommend an extra Redo stack, consisting
of everything that has been removed from the History stack since the last move.
To Redo, take the last item off the Redo stack and add it back to the History stack.
To Undo, take the last item off the History stack and add it to the Redo stack.
To make a move, add the new move (or board) to the History stack.
ABG