Index: yahtzee.c
===================================================================
--- yahtzee.c	(revision 6895)
+++ yahtzee.c	(working copy)
@@ -32,15 +32,19 @@
 #include <string.h>
 #include <unistd.h>
 #include <time.h>
+#include <glib.h>
 
 #include <config.h>
 #include "yahtzee.h"
 
 char *ProgramHeader = "Yahtzee Version 2.00 (c)1998 SDH, (c)1992 by zorst";
 
+GList *UndoList = NULL;
+GList *RedoList = NULL;
+UndoScoreElement lastRoll;
 
 /*=== Exported variables ===*/
-DiceInfo DiceValues[5] = { {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0} };
+DiceInfo DiceValues[NUMBER_OF_DICE] = { {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0} };
 Player players[MAX_NUMBER_OF_PLAYERS] = {
   {NULL, {0}, {0}, 0, 0},
   {NULL, {0}, {0}, 0, 0},
@@ -175,6 +179,7 @@
   CurrentPlayer = 0;
   NumberOfRolls = 0;
   LastHumanNumberOfRolls = 0;
+  FreeUndoRedoLists();
 
   NumberOfPlayers = NumberOfComputers + NumberOfHumans;
 
@@ -217,6 +222,7 @@
       DiceValues[i].val = RollDie ();
       DiceValues[i].sel = 0;
       cnt++;
+      lastRoll.DiceValues[i] = DiceValues[i].val;
     }
   }
 
@@ -224,6 +230,7 @@
   if(cnt == 0){
     for (i = 0; i < 5; i++) {
       DiceValues[i].val = RollDie ();
+      lastRoll.DiceValues[i] = DiceValues[i].val;
     }
   }
 
@@ -236,6 +243,8 @@
     say (_("Choose a score slot."));
   }
 
+  lastRoll.roll = NumberOfRolls;
+  lastRoll.player = CurrentPlayer;
 }
 
 int
@@ -377,7 +386,8 @@
   return (val);
 }
 
-static void play_score_kismet(int player, int field)
+static void
+play_score_kismet(int player, int field)
 {
   int i;
 
@@ -457,7 +467,8 @@
   }
 }
 
-static void play_score_yahtzee(int player, int field)
+static void
+play_score_yahtzee(int player, int field)
 {
   int i;
 
@@ -510,6 +521,123 @@
   }
 }
 
+void
+PrependUndoList(gint player, gint field, gint score) {
+    UndoScoreElement *elem = g_new0(UndoScoreElement, 1);
+    gint ii;
+    elem->player = player;
+    elem->field  = field;
+    elem->score  = score;
+
+    for (ii = 0; ii < NUMBER_OF_DICE; ii++)
+        elem->DiceValues[ii] = DiceValues[ii].val;
+    elem->roll = NumberOfRolls;
+
+    if (!players[player].comp)
+        FreeUndoList();
+    UndoList = g_list_prepend(UndoList, elem);
+}
+
+void
+ResetDiceState(UndoScoreElement *elem) {
+    gint ii;
+    for (ii = 0; ii < NUMBER_OF_DICE; ii++) {
+        DiceValues[ii].val = elem->DiceValues[ii];
+        DiceValues[ii].sel = 0;
+    }
+    NumberOfRolls = elem->roll;
+    ShowPlayer (elem->player, elem->field);
+}
+
+gint
+UndoLastMove() {
+    if (UndoList) {
+        UndoScoreElement *elem = UndoList->data;
+        players[elem->player].score[elem->field] = 0;
+        players[elem->player].used [elem->field] = 0;
+
+        ResetDiceState(elem);
+        UndoList = g_list_remove(UndoList, elem);
+        RedoList = g_list_prepend(RedoList, elem);
+        return elem->player;
+    }
+
+    return CurrentPlayer;
+}
+
+gint
+RedoLastMove() {
+    gint rval = (CurrentPlayer + 1) % NumberOfPlayers;
+    if (RedoList) {
+        gint ii;
+        UndoScoreElement *elem = RedoList->data;
+
+        for (ii = 0; ii < NUMBER_OF_DICE; ii++) {
+            DiceValues[ii].val = elem->DiceValues[ii];
+            DiceValues[ii].sel = 0;
+        }
+
+        RedoList = g_list_remove(RedoList, elem);
+        play_score(elem->player, elem->field);
+        rval = (elem->player + 1) % NumberOfPlayers;
+        g_free(elem);
+        if (RedoList) {
+            elem = RedoList->data;
+            rval = elem->player;
+        }
+    }
+
+    return rval;
+}
+
+void
+RestoreLastRoll() {
+    ResetDiceState(&lastRoll);
+}
+
+UndoScoreElement
+*RedoHead() {
+    if (RedoList) {
+        UndoScoreElement *elem = RedoList->data;
+        return elem;
+    }
+
+    return NULL;
+}
+
+void
+FreeUndoList() {
+    while (UndoList) {
+        UndoScoreElement *elem = UndoList->data;
+        UndoList = g_list_remove(UndoList, elem);
+        g_free(elem);
+    }
+}
+
+void
+FreeRedoList() {
+    while (RedoList) {
+        UndoScoreElement *elem = RedoList->data;
+        RedoList = g_list_remove(RedoList, elem);
+        g_free(elem);
+    }
+}
+
+void
+FreeUndoRedoLists() {
+    FreeUndoList();
+    FreeRedoList();
+}
+
+void
+FreeRedoListHead() {
+    if (RedoList) {
+        UndoScoreElement *elem = RedoList->data;
+        RedoList = g_list_remove(RedoList, elem);
+        g_free(elem);
+    }
+}
+
 /* Test if we can use suggested score slot */
 int
 play_score (int player, int field)
@@ -540,6 +668,7 @@
     fprintf(stderr, "Unexpected game type %d. Aborting...", game_type);
     exit(1);
   }
+  PrependUndoList(player, field, players[player].score[field]);
 
   ShowPlayer (player, field);
 
@@ -560,6 +689,7 @@
   int total;
 
   WinningScore = 0;
+  FreeUndoRedoLists();
 
   for (i = 0; i < NumberOfPlayers; ++i) {
     total = total_score (i);
@@ -581,7 +711,15 @@
   return winner;
 }
 
+int UndoPossible(void)
+{
+    return UndoList != NULL;
+}
 
+int RedoPossible(void)
+{
+    return RedoList != NULL;
+}
 
 void
 calc_random (void)
Index: clist.c
===================================================================
--- clist.c	(revision 6895)
+++ clist.c	(working copy)
@@ -134,10 +134,18 @@
       if (play_score (CurrentPlayer, row) == SLOT_USED) {
 	say (_("Already used! " "Where do you want to put that?"));
       } else {
-	NextPlayer ();
+        UndoScoreElement *elem = RedoHead();
+        /* If the player selects the same slot as before, */
+        /* Redo so they don't get a new roll              */
+        if (elem && elem->player == CurrentPlayer)
+          RedoPlayer();
+        else
+         NextPlayer ();
+
       }
     }
   }
+  update_undo_sensitivity();
 }
 
 static gboolean
Index: yahtzee.h
===================================================================
--- yahtzee.h	(revision 6895)
+++ yahtzee.h	(working copy)
@@ -61,6 +61,14 @@
   int sel;
 } DiceInfo;
 
+typedef struct undo_score_element_t {
+    int player;
+    int field;
+    int score;
+    int DiceValues[NUMBER_OF_DICE];
+    int roll;
+} UndoScoreElement;
+
 /* yahtzee.c */
 extern DiceInfo DiceValues[];
 extern Player players[];
@@ -101,8 +109,17 @@
 extern void RollSelectedDice (void);
 extern int GameIsOver (void);
 extern int FindWinner (void);
-int UndoPossible (void);
-
+extern int UndoLastMove(void);
+extern int RedoLastMove(void);
+void PrependUndoList(gint player, gint field, gint score);
+void FreeUndoList(void);
+void FreeRedoList(void);
+void FreeUndoRedoLists(void);
+void FreeRedoListHead(void);
+extern int  UndoPossible(void);
+extern int  RedoPossible(void);
+extern void ResetDiceState(UndoScoreElement *elem);
+extern void RestoreLastRoll(void);
 /* Computer.c */
 extern void ComputerRolling (int player);
 extern void ComputerScoring (int player);
@@ -112,8 +129,13 @@
 extern void UpdateAllDicePixmaps (void);
 extern void DeselectAllDice (void);
 extern void ShowPlayer (int num, int field);
+extern void DisplayCurrentPlayer(void);
+extern void DisplayCurrentPlayerRefreshDice(void);
 extern void NextPlayer (void);
+extern void PreviousPlayer (void);
+extern void RedoPlayer     (void);
 extern void ShowHighScores (void);
+extern UndoScoreElement *RedoHead(void);
 
 enum { SCORE_OK = 0, SLOT_USED, PLAYER_DONE, YAHTZEE_NEWGAME };
 
Index: gyahtzee.c
===================================================================
--- gyahtzee.c	(revision 6895)
+++ gyahtzee.c	(working copy)
@@ -96,6 +96,7 @@
 static GtkWidget *rollLabel;
 static GtkWidget *mbutton;
 static GtkAction *scores_action;
+static GtkAction *undo_action;
 static gchar *game_type_string = NULL;
 static gint   test_computer_play = 0;
 gint NUM_TRIALS = 0;
@@ -259,7 +260,31 @@
   return TRUE;
 }
 
+/* Show the current score and prompt for current player state */
+
 void
+DisplayCurrentPlayer() {
+  ShowoffPlayer (ScoreList, CurrentPlayer, 1);
+
+  if (players[CurrentPlayer].name) {
+    if (players[CurrentPlayer].comp) {
+      say (_("Computer playing for %s"), players[CurrentPlayer].name);
+    } else {
+      say (_("%s! -- You're up."), players[CurrentPlayer].name);
+    }
+  }
+}
+
+/* Display current player and refresh dice/display */
+void
+DisplayCurrentPlayerRefreshDice(void) {
+    DisplayCurrentPlayer();
+    UpdateAllDicePixmaps ();
+    DeselectAllDice ();
+    UpdateRollLabel();
+}
+
+void
 NextPlayer (void)
 {
   if (GameIsOver ()) {
@@ -287,18 +312,10 @@
     CurrentPlayer = (CurrentPlayer + 1) % NumberOfPlayers;
   } while (players[CurrentPlayer].finished);
 
-  ShowoffPlayer (ScoreList, CurrentPlayer, 1);
-
-  if (players[CurrentPlayer].name) {
-    if (players[CurrentPlayer].comp) {
-      say (_("Computer playing for %s"), players[CurrentPlayer].name);
-    } else {
-      say (_("%s! -- You're up."), players[CurrentPlayer].name);
-    }
-  }
-
+  DisplayCurrentPlayer();
   SelectAllDice ();
   RollSelectedDice ();
+  FreeRedoList();
 
   /* Remember the roll count if this turn is for a
      human player for display at the end of the game */
@@ -320,7 +337,48 @@
     UpdateRollLabel ();
 }
 
+/* Go back to the previous player */
+
 void
+PreviousPlayer(void)
+{
+    if (UndoPossible()) {
+        NumberOfRolls = 1;
+        ready_to_advance_player = FALSE;
+        ShowoffPlayer (ScoreList, CurrentPlayer, 0);
+
+  /* Find the next player with rolls left */
+        do {
+          CurrentPlayer = (UndoLastMove() + NumberOfPlayers) % NumberOfPlayers;
+        } while (players[CurrentPlayer].comp && UndoPossible());
+
+        DisplayCurrentPlayerRefreshDice();
+    }
+}
+
+void
+RedoPlayer(void)
+{
+    if (RedoPossible()) {
+        NumberOfRolls = 1;
+        ready_to_advance_player = FALSE;
+        ShowoffPlayer(ScoreList, CurrentPlayer, 0);
+        /* The first element of the list is the undone turn, so
+         * we need to remove it from the list before redoing other
+         * turns.                                                    */
+        FreeRedoListHead();
+
+        /* Redo all computer players */
+        do {
+            CurrentPlayer = RedoLastMove();
+        } while (players[CurrentPlayer].comp && RedoPossible());
+
+        RestoreLastRoll();
+        DisplayCurrentPlayerRefreshDice();
+    }
+}
+
+void
 ShowPlayer (int num, int field)
 {
   int i;
@@ -434,6 +492,7 @@
   gtk_label_set_label (GTK_LABEL (rollLabel), str->str);
 
   update_roll_button_sensitivity ();
+  update_undo_sensitivity ();
 }
 
 static void
@@ -506,6 +565,8 @@
 {
   if (!players[CurrentPlayer].comp) {
     RollSelectedDice ();
+    if (NumberOfRolls > 1)
+        FreeUndoRedoLists();
     UpdateRollLabel ();
     LastHumanNumberOfRolls = NumberOfRolls;
   }
@@ -589,6 +650,12 @@
   return FALSE;
 }
 
+static gint undo_callback(GtkAction *action, gpointer data)
+{
+    PreviousPlayer();
+    return FALSE;
+}
+
 static void
 LoadDicePixmaps (void)
 {
@@ -616,6 +683,11 @@
   gtk_action_set_sensitive (scores_action, top != NULL);
 }
 
+void update_undo_sensitivity(void)
+{
+    gtk_action_set_sensitive(undo_action, UndoPossible());
+}
+
 static void
 help_cb (GtkAction * action, gpointer data)
 {
@@ -629,6 +701,8 @@
   {"HelpMenu", NULL, N_("_Help")},
   {"NewGame", GAMES_STOCK_NEW_GAME, NULL, NULL, NULL,
    G_CALLBACK (new_game_callback)},
+  {"Undo", GAMES_STOCK_UNDO_MOVE, NULL, NULL, NULL,
+   G_CALLBACK (undo_callback)},
   {"Scores", GAMES_STOCK_SCORES, NULL, NULL, NULL,
    G_CALLBACK (score_callback)},
   {"Quit", GTK_STOCK_QUIT, NULL, NULL, NULL, G_CALLBACK (quit_game)},
@@ -646,6 +720,7 @@
   "  <menubar name='MainMenu'>"
   "    <menu action='GameMenu'>"
   "      <menuitem action='NewGame'/>"
+  "      <menuitem action='Undo'/>"
   "      <menuitem action='Scores'/>"
   "      <menuitem action='Quit'/>"
   "    </menu>"
@@ -672,6 +747,8 @@
   gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
   gtk_ui_manager_add_ui_from_string (ui_manager, ui_description, -1, NULL);
   scores_action = gtk_action_group_get_action (action_group, "Scores");
+  undo_action   = gtk_action_group_get_action (action_group, "Undo");
+  update_undo_sensitivity();
 }
 
 
Index: gyahtzee.h
===================================================================
--- gyahtzee.h	(revision 6895)
+++ gyahtzee.h	(working copy)
@@ -46,7 +46,7 @@
 /* gyahtzee.c */
 extern int GyahtzeeAbort;
 extern GtkWidget *ScoreList;
-
+extern void update_undo_sensitivity(void);
 #endif /* _Gyahtzee_H_ */
 
 /* Arrgh - lets all use the same tabs under emacs: 
