Next we will include the ability to undo moves. Not just single moves but all the way back to the deal. We need to keep a list of the states of the game, from the beginning to the current move. That is why you saw calls to a new function recordHistory()
on the previous page.
It is a convention in Windows that the key combination Ctrl+Z means "undo" in many applications. On a Mac I guess that would be Cmd+Z and I am hoping that Linux and other systems will do something similar. So we will enable our game to recognise that key combination.
At the end of the constructor function for Spider
add another listener, this time attached to the document rather than the canvas:
document.addEventListener ("keydown", onKeydown);
And to satisfy that we need another handler function in the Spider file. Keep it very simple for now:
function onKeydown (e)
{ if (e.key === "z" && e.ctrlKey) undo ();
}
Then we need the function undo()
. But first consider how it must work. There needs to be a list of states of the game. Just an array of states. Then undo()
can simply remove the last one and redraw the remaining last state.
We will use another new object type: State
, in a new script file following the usual pattern. This is what I wrote and it should be fairly self-explanatory:
function State ()
{ this.columns = [];
for (var i = 0; i < GAME.N_COLUMNS; i++)
{ var col = [];
var list = GAME.COLUMNS [i].getSublist (0);
// list of cards in the whole column
for (var j = 0; j < list.length; j++)
{ var card = list [j];
col.push ({card:card, up:card.faceUp});
// faceUp is the only property of card that may change
}
this.columns.push (col);
}
}
function recordHistory ()
{ GAME.HISTORY.push (new State ());
}
function undo ()
{ if (0 === GAME.HISTORY.length) return;
var state = GAME.HISTORY.pop ();
for (var i = 0; i < state.columns.length; i++)
{ var srcCol = state.columns [i];
GAME.COLUMNS [i].empty ();
for (var j = 0; j < srcCol.length; j++)
{ var sj = srcCol [j];
var card = sj.card;
card.faceUp = sj.up;
GAME.COLUMNS [i].addCard (card);
} }
GAME.game.draw ();
}
Creating each State
reuses Column.getSublist()
but this time getting the whole column. The only tricky part is to realise two things:
Deck
has been created its cards do not get deleted or recreated. They change position and they can switch between face down and face up. So we can record a list of references to cards but we also need to record whether they are face up.Column.addCard()
sets the new card position so we do not need to record that in the State.You can see that recordHistory()
is then very simple. Our undo()
moves cards to the recorded states and then redraws the canvas.