Next page | Contents page |

Undoing previous moves: history

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:

You can see that recordHistory() is then very simple. Our undo() moves cards to the recorded states and then redraws the canvas.

Next page | Contents page |