Index: AUTHORS
===================================================================
--- AUTHORS	(revision 5970)
+++ AUTHORS	(working copy)
@@ -20,3 +20,4 @@
 Andreas Røsdal <andreasr@gnome.org>
 Robert Ancell <robert.ancell@gmail.com>
 Thomas Hinkle <Thomas_Hinkle@alumni.brown.edu>
+Jonathan Bastien-Filiatrault <joe@x2a.org>
Index: aisleriot/dialog.c
===================================================================
--- aisleriot/dialog.c	(revision 5970)
+++ aisleriot/dialog.c	(working copy)
@@ -61,7 +61,8 @@
 			    GTK_STOCK_UNDO, GTK_RESPONSE_REJECT, NULL);
   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
 			  GTK_STOCK_QUIT, GTK_RESPONSE_CLOSE,
-			  _("_New Game"), GTK_RESPONSE_ACCEPT, NULL);
+			  _("_New Game"), GTK_RESPONSE_ACCEPT,
+ 			  _("Choose s_eed"), GTK_RESPONSE_APPLY, NULL);
   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
 
   /* add a stock icon? */
@@ -70,6 +71,10 @@
     gtk_widget_destroy (dialog);
     new_game (NULL, NULL);
     break;
+  case GTK_RESPONSE_APPLY:
+    gtk_widget_destroy (dialog);
+    choose_seed_dialog();
+    break;
   case GTK_RESPONSE_REJECT:
     timer_restart ();
     gtk_widget_destroy (dialog);
@@ -97,6 +102,51 @@
   }
 }
 
+void choose_seed_dialog() {
+  GtkWidget* dialog;
+  GtkWidget* seed_spinner;
+  GtkWidget* label;
+  guint seed;
+  gchar* message;
+
+  dialog = gtk_dialog_new_with_buttons (_("Choose a seed"),
+					GTK_WINDOW (app),
+					GTK_DIALOG_DESTROY_WITH_PARENT,
+					GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+					GTK_STOCK_OK, GTK_RESPONSE_OK,
+					NULL);
+  gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+  message = g_strdup_printf (_("Choose a seed from 0 to %u"), G_MAXUINT);
+  label = gtk_label_new(message);
+  g_free (message);
+  gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
+		     label);
+  gtk_widget_show (label);
+
+  seed_spinner = gtk_spin_button_new_with_range(0, G_MAXUINT, 1);
+  gtk_entry_set_activates_default (GTK_ENTRY (seed_spinner), TRUE);
+  gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
+		     seed_spinner);
+  gtk_widget_show (seed_spinner);
+
+  switch (gtk_dialog_run (GTK_DIALOG (dialog)))
+    {
+    case GTK_RESPONSE_OK:
+      seed = (guint)gtk_spin_button_get_value ( GTK_SPIN_BUTTON (seed_spinner));
+      gtk_widget_destroy (label);
+      gtk_widget_destroy (seed_spinner);
+      gtk_widget_destroy (dialog);
+      new_game (NULL, &seed);
+      break;
+    default:
+      gtk_widget_destroy (label);
+      gtk_widget_destroy (seed_spinner);
+      gtk_widget_destroy (dialog);
+      break;
+    }
+}
+
 gchar *filename = NULL;
 
 static void
Index: aisleriot/dialog.h
===================================================================
--- aisleriot/dialog.h	(revision 5970)
+++ aisleriot/dialog.h	(working copy)
@@ -22,6 +22,7 @@
 
 void show_select_game_dialog (void);
 void show_game_over_dialog (void);
+void choose_seed_dialog (void);
 void show_preferences_dialog (void);
 void show_rules_options_dialog (void);
 void show_global_stats_dialog (void);
Index: aisleriot/menu.c
===================================================================
--- aisleriot/menu.c	(revision 5970)
+++ aisleriot/menu.c	(working copy)
@@ -68,6 +68,13 @@
   new_game (NULL, NULL);
 };
 
+static void
+new_game_seed_action ()
+{
+  if (waiting_for_mouse_up())
+    return;
+  choose_seed_dialog ();
+}
 
 static void
 new_recent_game_action (GtkAction * action, gpointer user_data)
@@ -250,6 +257,8 @@
 
   {"NewGame", GAMES_STOCK_NEW_GAME, NULL, NULL, N_("Start a new game"),
    G_CALLBACK (new_game_action)},
+  { "NewGameSeed", GTK_STOCK_OPEN, N_("Choose s_eed"), "<Ctrl>e",
+    N_("Start a new game with a specific seed"), G_CALLBACK (new_game_seed_action) },
   {"RestartGame", GAMES_STOCK_RESTART_GAME, NULL, NULL,
    N_("Restart the current game"), G_CALLBACK (restart_game)},
   {"Select", GTK_STOCK_INDEX, N_("_Select Game..."), "<Ctrl>o",
@@ -292,6 +301,7 @@
   "  <menubar name='MainMenu'>"
   "    <menu action='GameMenu'>"
   "      <menuitem action='NewGame'/>"
+  "      <menuitem action='NewGameSeed'/>"
   "      <menuitem action='RestartGame'/>"
   "      <menuitem action='Select'/>"
   "      <menu action='RecentlyPlayed'/>"
@@ -322,6 +332,7 @@
   "  </menubar>"
   "  <toolbar name='Toolbar'>"
   "    <toolitem action='NewGame'/>"
+  "    <toolitem action='NewGameSeed'/>"
   "    <toolitem action='RestartGame'/>"
   "    <toolitem action='Select'/>"
   "    <separator/>"
Index: aisleriot/rules/freecell.scm
===================================================================
--- aisleriot/rules/freecell.scm	(revision 5970)
+++ aisleriot/rules/freecell.scm	(working copy)
@@ -81,6 +81,49 @@
 ;; Utilities
 ;;
 
+(define redmond-seed 0)
+
+(define (redmond-srand s)
+  (set! redmond-seed s))
+
+;; Redmond PRNG, parameters from http://www.codeproject.com/useritems/PRNG.asp
+(define (redmond-rand)
+  (set! redmond-seed
+	(+ (* redmond-seed 214013) 2531011))
+  (modulo (ash redmond-seed -16)
+	  (expt 2 15)))
+
+(define (make-redmond-deck)
+  (set! DECK (make-deck-list-redmond 0)))
+
+;; We need the cards in AC AD AH AS 2C 2D 2H 2S 3C... order
+(define (make-deck-list-redmond value)
+  (if (eq? value 51)
+      (list (make-card (+ (quotient value 4) 1) (modulo value 4)))
+      (cons (make-card (+ (quotient value 4) 1) (modulo value 4))
+	    (make-deck-list-redmond (+ 1 value)))))
+
+(define (shuffle-deck-redmond gamenum)
+  (redmond-srand gamenum)
+  (set! DECK (shuffle-deck-list-redmond DECK)))
+
+;; Shuffle the deck with the Redmond PRNG,
+;; inspired by http://www.solitairelaboratory.com/mshuffle.txt
+(define (shuffle-deck-list-redmond deck)
+  (let ((n (modulo (redmond-rand) (length deck))))
+    (if (eq? 1 (length deck))
+	deck
+	(cons (list-ref deck n )
+	      (shuffle-deck-list-redmond
+	       (redmond-adjust-list deck n))))))
+
+(define (redmond-adjust-list deck n)
+  (if (eq? n (- (length deck) 1))
+      (list-head deck (- (length deck) 1))
+      (append (list-head deck n)
+	      (last-pair deck)
+	      (cdr (list-tail (list-head deck (- (length deck) 1)) n)))))
+
 (define (freecell? slot)
   (and (>= slot freecell-1) (<= slot freecell-4)))
 
@@ -318,8 +361,8 @@
 (define (new-game)
   (initialize-playing-area)
   (set-ace-low)
-  (make-standard-deck)
-  (shuffle-deck)
+  (make-redmond-deck)
+  (shuffle-deck-redmond SEED)
   
   ;; set up the board
 
Index: aisleriot/sol.c
===================================================================
--- aisleriot/sol.c	(revision 5970)
+++ aisleriot/sol.c	(working copy)
@@ -249,6 +249,7 @@
 new_game (gchar * file, guint * seedp)
 {
   double width, height;
+  char *title;
 
   /* If we're aborting an old game count it as a failure for
    * statistical purposes. */
@@ -296,6 +297,8 @@
   set_score (0);
   timer_reset ();
 
+  scm_call_1 (scm_variable_ref (scm_c_lookup ("set-seed")), scm_i_uint2big (seed));
+
   cscmi_start_game_lambda (&width, &height);
   scm_c_eval_string ("(start-game)");
 
@@ -313,7 +316,9 @@
     redo_set_sensitive (FALSE);
 
     game_over = FALSE;
-    gtk_window_set_title (GTK_WINDOW (app), _(game_name));
+    title = g_strdup_printf("%s #%u", _(game_name), seed);
+    gtk_window_set_title (GTK_WINDOW (app), title);
+    g_free(title);
   }
   /* We've just started a new game. Add this to the list of games the user likes */
   add_recently_played_game (file);
Index: aisleriot/sol.scm
===================================================================
--- aisleriot/sol.scm	(revision 5970)
+++ aisleriot/sol.scm	(working copy)
@@ -533,12 +533,17 @@
 (define HISTORY '())
 (define FUTURE '())
 (define IN-GAME #f)
+(define SEED #f)
 
 ; called from C:
 (define (start-game)
   (set! IN-GAME #t))
 
 ; called from C:
+(define (set-seed seed)
+  (set! SEED seed))
+
+; called from C:
 (define (end-move)
   (if (not (= 0 (length MOVE)))
       (begin
