Search code examples
javastackundotic-tac-toe

Java - Using multiple stacks to allow an 'undo' method


I'm working on a tic tac toe game and am wanting to implement an undo method. What I feel is the best way to do this is just setup another (multiple?) stacks, and make a copy of the 'moves' that have just taken place. Then, if undo is called, just pop the last move and repopulate the game board.

So yeah, I have the idea, but can't figure out how to implement it.

Some of what I have:

To set:

public void set(Position p, int v, int n) throws IOException { 
    if (board[p.x][p.y][p.z]!= 0) throw new IOException("Position taken");

    //Restrict 222 until all other's have been used
    if (n != 26) {
        if (p.x == 1 && p.y == 1 && p.z ==1) {
            throw new IOException("[2,2,2] cannot be played until all other positions have been taken");
        }
    }

    //Enforce x=1 for first 9, x=3 for next 9
    if (n < 9 ) {
        if (p.x != 0) throw new IOException("Please play on x=1 for the first 9 moves");
    }

    if (n >= 9 && n < 18) {
        if (p.x != 2) throw new IOException("Please play on x=3 for the first 9 moves");
    }

    board[p.x][p.y][p.z] = v;
}

Then there is a board method to build the board, a display method, and of course one to check for 3 in a row.

Thanks for any advice


Solution

  • There is design pattern to do undo and redo. The command design pattern. It involes

    public interface ICommand{
       void execute();
       void undo();
       void redo();
    }
    

    implement the above interface to perform your move, execute will encapsulate your action.

    class MoveCommand implements ICommand{//parameter to store current board state
      public MoveCommand(){
        // new board state is validated
      }
      public void execute(){
        // change the board state
      }
     public void undo(){ // restore
     }
     public void redo(){ // apply again if possible
     }
    }
    

    now create a new class that will be CommandDispatcher

    class CommandDispatcher{
     private List<ICommand> commands = new ArrayList<ICommand>();
     public CommandDispatcher(){
     }
     private ICommand currentCommand = null;
     public void setCommand(ICommand cmd){
       currentCommand  = cmd;
       cmd.execute();
       commands.add(cmd);
     }
     public void undoAll(){
        for(ICommand cmd : commands){cmd.undo();}
    }
     public void undo(){
     commands.remove(commands.size()-1);
     currentCommand = commands.get(commands.size()-1)
    }
    public void redo(){
    if(null!=currentCommand) currentCommand.redo();
    }
    

    }

    This way you can preserve the state of your application and prevent yourself from getting nullpointer exceptions. The method redo() will call execute() method. I just added it for clarity.