Index: yahtzee.c
===================================================================
--- yahtzee.c	(revision 6486)
+++ 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;
+  FreeUndoList();
 
   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
@@ -510,6 +519,112 @@
   }
 }
 
+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;
+
+    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);
+        g_free(elem);
+        rval = (elem->player + 1) % NumberOfPlayers;
+        if (RedoList) {
+            elem = RedoList->data;
+            rval = elem->player;
+            ResetDiceState(elem);
+        }
+    }
+    else {
+        if (rval == lastRoll.player) {
+            ResetDiceState(&lastRoll);
+        }
+    }
+
+    return rval;
+}
+
+void RestoreRedoListHead() {
+    if (RedoList) {
+        UndoScoreElement *elem = RedoList->data;
+        ResetDiceState(elem);
+    }
+}
+
+UndoScoreElement *RedoHead() {
+    if (RedoList) {
+        UndoScoreElement *elem = RedoList->data;
+        return elem;
+    }
+
+    return NULL;
+}
+
+void
+FreeUndoList() {
+    while (UndoList) {
+        UndoScoreElement *elem = UndoList->data;
+        g_free(elem);
+        UndoList = g_list_remove(UndoList, elem);
+    }
+    FreeRedoList();
+}
+
+void FreeRedoList() {
+    while (RedoList) {
+        UndoScoreElement *elem = RedoList->data;
+        g_free(elem);
+        RedoList = g_list_remove(RedoList, elem);
+    }
+}
+
 /* Test if we can use suggested score slot */
 int
 play_score (int player, int field)
@@ -540,6 +655,7 @@
     fprintf(stderr, "Unexpected game type %d. Aborting...", game_type);
     exit(1);
   }
+  PrependUndoList(player, field, players[player].score[field]);
 
   ShowPlayer (player, field);
 
@@ -560,6 +676,7 @@
   int total;
 
   WinningScore = 0;
+  FreeUndoList();
 
   for (i = 0; i < NumberOfPlayers; ++i) {
     total = total_score (i);
@@ -581,7 +698,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 6486)
+++ clist.c	(working copy)
@@ -134,10 +134,17 @@
       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 && elem->field == row)
+          RedoPlayer();
+        else
+	      NextPlayer ();
       }
     }
   }
+  update_undo_sensitivity();
 }
 
 static gboolean
Index: yahtzee.h
===================================================================
--- yahtzee.h	(revision 6486)
+++ 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,15 @@
 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);
+extern int  UndoPossible(void);
+extern int  RedoPossible(void);
+extern void ResetDiceState(UndoScoreElement *elem);
+extern void RestoreRedoListHead(void);
 /* Computer.c */
 extern void ComputerRolling (int player);
 extern void ComputerScoring (int player);
@@ -113,7 +128,10 @@
 extern void DeselectAllDice (void);
 extern void ShowPlayer (int num, int field);
 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 6486)
+++ gyahtzee.c	(working copy)
@@ -96,6 +96,8 @@
 static GtkWidget *rollLabel;
 static GtkWidget *mbutton;
 static GtkAction *scores_action;
+static GtkAction *undo_action;
+static GtkAction *redo_action;
 static gchar *game_type_string = NULL;
 static gint   test_computer_play = 0;
 gint NUM_TRIALS = 0;
@@ -273,6 +275,7 @@
         NumberOfRolls = LastHumanNumberOfRolls;
     /* update_roll_button_sensitivity() needs to be called in
        this context however UpdateRollLabel() also calls that method */
+    FreeUndoList();
     UpdateRollLabel ();
     CheerWinner ();
     return;
@@ -299,6 +302,7 @@
 
   SelectAllDice ();
   RollSelectedDice ();
+  FreeRedoList();
 
   /* Remember the roll count if this turn is for a
      human player for display at the end of the game */
@@ -320,6 +324,74 @@
     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);
+
+        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);
+            }
+        }
+
+        UpdateAllDicePixmaps ();
+        DeselectAllDice ();
+        UpdateRollLabel();
+        update_undo_sensitivity();
+    }
+}
+
+void RedoPlayer(void)
+{
+    if (RedoPossible()) {
+        NumberOfRolls = 1;
+        ready_to_advance_player = FALSE;
+        ShowoffPlayer (ScoreList, CurrentPlayer, 0);
+
+  /* Find the next player with rolls left */
+        do {
+          CurrentPlayer = RedoLastMove();
+        } while (players[CurrentPlayer].comp);
+
+/*      if (!RedoPossible()) {
+            SelectAllDice();
+            RollSelectedDice();
+        }
+        else { */
+        if (RedoPossible()) {
+            RestoreRedoListHead();
+        }
+        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);
+            }
+        }
+
+        UpdateAllDicePixmaps ();
+        DeselectAllDice ();
+        UpdateRollLabel();
+        update_undo_sensitivity();
+    }
+}
+
 void
 ShowPlayer (int num, int field)
 {
@@ -587,6 +659,18 @@
   return FALSE;
 }
 
+static gint undo_callback(GtkAction *action, gpointer data)
+{
+    PreviousPlayer();
+    return FALSE;
+}
+
+static gint redo_callback(GtkAction *action, gpointer data)
+{
+    RedoPlayer();
+    return FALSE;
+}
+
 static void
 LoadDicePixmaps (void)
 {
@@ -614,6 +698,12 @@
   gtk_action_set_sensitive (scores_action, top != NULL);
 }
 
+void update_undo_sensitivity(void)
+{
+    gtk_action_set_sensitive(undo_action, UndoPossible());
+    gtk_action_set_sensitive(redo_action, RedoPossible());
+}
+
 static void
 help_cb (GtkAction * action, gpointer data)
 {
@@ -627,6 +717,10 @@
   {"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)},
+  {"Redo", GAMES_STOCK_REDO_MOVE, NULL, NULL, NULL,
+   G_CALLBACK (redo_callback)},
   {"Scores", GAMES_STOCK_SCORES, NULL, NULL, NULL,
    G_CALLBACK (score_callback)},
   {"Quit", GTK_STOCK_QUIT, NULL, NULL, NULL, G_CALLBACK (quit_game)},
@@ -644,6 +738,8 @@
   "  <menubar name='MainMenu'>"
   "    <menu action='GameMenu'>"
   "      <menuitem action='NewGame'/>"
+  "      <menuitem action='Undo'/>"
+  "      <menuitem action='Redo'/>"
   "      <menuitem action='Scores'/>"
   "      <menuitem action='Quit'/>"
   "    </menu>"
@@ -670,6 +766,9 @@
   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");
+  redo_action   = gtk_action_group_get_action (action_group, "Redo");
+  update_undo_sensitivity();
 }
 
 
Index: gyahtzee.h
===================================================================
--- gyahtzee.h	(revision 6486)
+++ 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: 
