[aisleriot/gnome-3-2] Fix game restart



commit a46f0a573ce4ca142dd02a201817508dbf594327
Author: Christian Persch <chpe gnome org>
Date:   Sun Sep 18 23:10:04 2011 +0200

    Fix game restart
    
    Copy the RNG state, and use the saved state when restarting a game.
    
    Remove the --seed option which is useless since while we can create
    a GRand for a given seed, there is no way to serialise a GRand.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=657735

 src/game.c   |  113 +++++++++++++++++++++++++++++++++++++++++----------------
 src/game.h   |    7 ++--
 src/sol.c    |   20 ++++------
 src/window.c |   57 ++++++++++++++++++-----------
 src/window.h |    3 +-
 5 files changed, 131 insertions(+), 69 deletions(-)
---
diff --git a/src/game.c b/src/game.c
index cc2181d..bd90652 100644
--- a/src/game.c
+++ b/src/game.c
@@ -70,7 +70,10 @@ struct _AisleriotGame
   GPtrArray *slots;
 
   char *game_file;
-  guint32 seed;
+
+  GRand *rand;
+  GRand *saved_rand;
+
   guint delayed_call_timeout_id;
 
   GTimer *timer;
@@ -294,7 +297,9 @@ cscmi_exception_get_backtrace (SCM tag, SCM throw_args)
   message = g_string_sized_new (1024);
 
   g_string_append_printf (message, "Variation: %s\n", aisleriot_game_get_game_file (game));
+#if 0
   g_string_append_printf (message, "Seed: %u\n", game->seed);
+#endif
 
   g_string_append (message, "Scheme error:\n\t");
 
@@ -908,7 +913,9 @@ scm_set_lambda (SCM start_game_lambda,
 static SCM
 scm_myrandom (SCM range)
 {
-  return scm_from_uint32 (g_random_int_range (0, scm_to_int (range)));
+  AisleriotGame *game = app_game;
+
+  return scm_from_uint32 (g_rand_int_range (game->rand, 0, scm_to_int (range)));
 }
 
 static SCM
@@ -1100,6 +1107,8 @@ cscmi_winning_game_lambda (void)
 static void
 aisleriot_game_init (AisleriotGame *game)
 {
+  game->rand = game->saved_rand = NULL;
+
   game->state = GAME_UNINITIALISED;
 
   game->slots = g_ptr_array_sized_new (SLOT_CARDS_N_PREALLOC);
@@ -1145,6 +1154,11 @@ aisleriot_game_finalize (GObject *object)
 
   g_timer_destroy (game->timer);
 
+  if (game->rand)
+    g_rand_free (game->rand);
+  if (game->saved_rand)
+    g_rand_free (game->saved_rand);
+
   app_game = NULL;
 
   G_OBJECT_CLASS (aisleriot_game_parent_class)->finalize (object);
@@ -1710,6 +1724,8 @@ game_scm_new_game (void *user_data)
   AisleriotGame *game = user_data;
   gboolean game_over;
 
+  g_assert (game->rand != NULL);
+
   /* It is possible for some games to not have any moves right from the
    * start. If this happens we redeal.
    */
@@ -1717,6 +1733,11 @@ game_scm_new_game (void *user_data)
   do {
     SCM size, lambda, over;
 
+    /* Copy RNG state */
+    if (game->saved_rand)
+      g_rand_free (game->saved_rand);
+    game->saved_rand = g_rand_copy (game->rand);
+
     size = scm_call_0 (game->lambdas[START_GAME_LAMBDA]);
     game->width = scm_to_double (SCM_CAR (size));
     game->height = scm_to_double (SCM_CADR (size));
@@ -1742,18 +1763,20 @@ game_scm_new_game (void *user_data)
   return SCM_BOOL_T;
 }
 
-/**
- * aisleriot_game_new_game:
+/*
+ * aisleriot_game_new_game_internal:
  * @game:
- * @seed: a pointer to a #guint, or %NULL
+ * @rand: (allow-none) (transfer full): a #GRand, or %NULL
+ * @update_statistics: whether to update statistic
  *
  * Starts a new game of the currently loaded game type.
- * If @seed is non-%NULL, the value it points to is used
- * as the game number; otherwise a random number is used.
+ *
+ * @game will take ownership of @rand.
  */
-void
-aisleriot_game_new_game (AisleriotGame *game,
-                         guint *seed)
+static void
+aisleriot_game_new_game_internal (AisleriotGame *game,
+                                  GRand *rand,
+                                  gboolean count_loss)
 {
   GObject *object = G_OBJECT (game);
   GError *err = NULL;
@@ -1774,17 +1797,24 @@ aisleriot_game_new_game (AisleriotGame *game,
   /* FIXMEchpe: this allows cheating the statistics by doing
    * Restart, then New Game.
    */
-  if (game->state >= GAME_RUNNING &&
-      (!seed || *seed != game->seed)) {
+  if (count_loss &&
+      game->state >= GAME_RUNNING) {
     update_statistics (game);
   }
 
-  if (seed) {
-    game->seed = *seed;
-  } else {
-    game->seed = 0;
+  if (rand != NULL) {
+    if (game->rand)
+      g_rand_free (game->rand);
+
+    game->rand = rand; /* adopted */
+  } else if (game->rand == NULL) {
+    game->rand = g_rand_new ();
   }
 
+  if (game->saved_rand)
+    g_rand_free (game->saved_rand);
+  game->saved_rand = NULL;
+
   clear_delayed_call (game);
   /* The game isn't actually in progress until the user makes a move */
   set_game_state (game, GAME_BEGIN);
@@ -1811,6 +1841,36 @@ aisleriot_game_new_game (AisleriotGame *game,
 }
 
 /**
+ * aisleriot_game_new_game:
+ * @game:
+ *
+ * Starts a new game of the currently loaded game type.
+ *
+ * @game will take ownership of @rand.
+ */
+void
+aisleriot_game_new_game (AisleriotGame *game)
+{
+  aisleriot_game_new_game_internal (game, NULL, TRUE);
+}
+
+/**
+ * aisleriot_game_new_game_with_rand:
+ * @game:
+ * @rand: (allow-none) (transfer full): a #GRand, or %NULL
+ *
+ * Starts a new game of the currently loaded game type.
+ *
+ * @game will take ownership of @rand.
+ */
+void
+aisleriot_game_new_game_with_rand (AisleriotGame *game,
+                                   GRand *rand)
+{
+  aisleriot_game_new_game_internal (game, rand, TRUE);
+}
+
+/**
  * aisleriot_game_restart_game:
  * @game:
  *
@@ -1819,9 +1879,12 @@ aisleriot_game_new_game (AisleriotGame *game,
 void
 aisleriot_game_restart_game (AisleriotGame *game)
 {
-  guint seed = game->seed;
+  GRand *rand;
+
+  rand = game->saved_rand;
+  game->saved_rand = NULL;
 
-  aisleriot_game_new_game (game, &seed);
+  aisleriot_game_new_game_internal (game, rand, FALSE);
 }
 
 /**
@@ -1871,20 +1934,6 @@ aisleriot_game_get_geometry (AisleriotGame *game,
 }
 
 /**
- * aisleriot_game_get_seed:
- * @game:
- *
- * Returns the seed of the current game.
- *
- * Returns: a number
- */
-guint
-aisleriot_game_get_seed (AisleriotGame *game)
-{
-  return game->seed;
-}
-
-/**
  * aisleriot_game_drag_valid:
  * @game:
  * @slot_id:
diff --git a/src/game.h b/src/game.h
index 6d7781e..6b060df 100644
--- a/src/game.h
+++ b/src/game.h
@@ -165,7 +165,10 @@ void aisleriot_game_redo_move (AisleriotGame * game);
 gboolean aisleriot_game_load_game (AisleriotGame * game,
                                    const char *filename, GError ** error);
 
-void aisleriot_game_new_game (AisleriotGame * game, guint * seed);
+void aisleriot_game_new_game (AisleriotGame *game);
+
+void aisleriot_game_new_game_with_rand (AisleriotGame *game,
+                                        GRand *rand);
 
 void aisleriot_game_restart_game (AisleriotGame * game);
 
@@ -173,8 +176,6 @@ const char *aisleriot_game_get_game_file (AisleriotGame * game);
 
 char *aisleriot_game_get_name (AisleriotGame * game);
 
-guint aisleriot_game_get_seed (AisleriotGame * game);
-
 gboolean aisleriot_game_drag_valid (AisleriotGame * game,
                                     int slot_id,
                                     guint8 * cards, guint n_cards);
diff --git a/src/sol.c b/src/sol.c
index 77c9573..c868415 100644
--- a/src/sol.c
+++ b/src/sol.c
@@ -52,7 +52,7 @@ N_("About Solitaire")
 typedef struct {
   AisleriotWindow *window;
   char *variation;
-  guint seed;
+  gint seed; /* unused */
   gboolean freecell;
 } AppData;
 
@@ -66,13 +66,11 @@ save_state_cb (EggSMClient *client,
   AisleriotGame *game;
   char *argv[5];
   const char *game_name;
-  char *seed;
   int argc = 0;
 
   game = aisleriot_window_get_game (data->window);
 
   game_name = aisleriot_game_get_game_file (game);
-  seed = g_strdup_printf ("%u", aisleriot_game_get_seed (game));
 
   argv[argc++] = g_get_prgname ();
 
@@ -83,14 +81,9 @@ save_state_cb (EggSMClient *client,
     argv[argc++] = (char *) game_name;
   }
 
-  argv[argc++] = (char *) "--seed";
-  argv[argc++] = seed;
-
   /* FIXMEchpe: save game state too? */
 
   egg_sm_client_set_restart_command (client, argc, (const char **) argv);
-
-  g_free (seed);
 }
 
 static void
@@ -110,10 +103,13 @@ add_main_options (GOptionContext *option_context,
   const GOptionEntry aisleriot_options[] = {
     { "variation", 'v', 0, G_OPTION_ARG_STRING, &data->variation,
       N_("Select the game type to play"), N_("NAME") },
-    { "seed", 's', 0, G_OPTION_ARG_STRING, &data->seed,
-      N_("Select the game number"), N_("NUMBER") },
     { "freecell", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &data->freecell,
       NULL, NULL },
+
+    /* Ignored option, for backward compat with saved session */
+    { "seed", 's', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, &data->seed,
+      NULL, NULL },
+
     { NULL }
   };
 
@@ -195,9 +191,9 @@ main_prog (void *closure, int argc, char *argv[])
 #endif /* WITH_SMCLIENT */
 
   if (data.freecell) {
-    aisleriot_window_set_game (data.window, FREECELL_VARIATION, data.seed);
+    aisleriot_window_set_game (data.window, FREECELL_VARIATION, NULL);
   } else {
-    aisleriot_window_set_game (data.window, data.variation, data.seed);
+    aisleriot_window_set_game (data.window, data.variation, NULL);
   }
 
   gtk_window_present (GTK_WINDOW (data.window));
diff --git a/src/window.c b/src/window.c
index d00ad2b..89df4be 100644
--- a/src/window.c
+++ b/src/window.c
@@ -173,7 +173,7 @@ game_over_dialog_response_cb (GtkWidget *dialog,
       aisleriot_game_restart_game (priv->game);
       break;
     case RESPONSE_NEW_GAME:
-      aisleriot_game_new_game (priv->game, NULL);
+      aisleriot_game_new_game (priv->game);
       break;
     case GTK_RESPONSE_CLOSE:
       gtk_widget_destroy (GTK_WIDGET (window)); /* this will quit */
@@ -186,7 +186,7 @@ game_over_dialog_response_cb (GtkWidget *dialog,
        * thing to do.
        */
       if (game_won) {
-        aisleriot_game_new_game (priv->game, NULL);
+        aisleriot_game_new_game (priv->game);
       } else {
         aisleriot_game_undo_move (priv->game);
       }
@@ -307,7 +307,7 @@ new_game_cb (GtkAction *action,
 {
   AisleriotWindowPrivate *priv = window->priv;
 
-  aisleriot_game_new_game (priv->game, NULL);
+  aisleriot_game_new_game (priv->game);
 
   gtk_widget_grab_focus (GTK_WIDGET (priv->board));
 }
@@ -627,7 +627,7 @@ debug_cycle_timeout_cb (AisleriotWindow *window)
     return FALSE;
 
   game_file = data->current_game->data;  
-  aisleriot_window_set_game (data->window, game_file, 0);
+  aisleriot_window_set_game (data->window, game_file, NULL);
 
   return TRUE;
 }
@@ -650,7 +650,7 @@ debug_game_first (GtkAction *action,
   if (!data->current_game)
     return;
 
-  aisleriot_window_set_game (data->window, (const char *) data->current_game->data, 0);
+  aisleriot_window_set_game (data->window, (const char *) data->current_game->data, NULL);
 }
 
 static void
@@ -664,7 +664,7 @@ debug_game_last (GtkAction *action,
   if (!data->current_game)
     return;
 
-  aisleriot_window_set_game (data->window, (const char *) data->current_game->data, 0);
+  aisleriot_window_set_game (data->window, (const char *) data->current_game->data, NULL);
 }
 
 static void
@@ -683,7 +683,7 @@ debug_game_next (GtkAction *action,
   if (!data->current_game)
     return;
 
-  aisleriot_window_set_game (data->window, (const char *) data->current_game->data, 0);
+  aisleriot_window_set_game (data->window, (const char *) data->current_game->data, NULL);
 }
 
 static void
@@ -702,7 +702,7 @@ debug_game_prev (GtkAction *action,
   if (!data->current_game)
     return;
 
-  aisleriot_window_set_game (data->window, (const char *) data->current_game->data, 0);
+  aisleriot_window_set_game (data->window, (const char *) data->current_game->data, NULL);
 }
 
 static void
@@ -716,6 +716,7 @@ debug_choose_seed_response_cb (GtkWidget *dialog,
     const char *text;
     char *endptr;
     guint seed;
+    GRand *rand;
 
     entry = g_object_get_data (G_OBJECT (dialog), "entry");
     text = gtk_entry_get_text (entry);
@@ -723,7 +724,9 @@ debug_choose_seed_response_cb (GtkWidget *dialog,
     errno = 0;
     seed = g_ascii_strtoull (text, &endptr, 10);
     if (errno == 0 && endptr != text) {
-      aisleriot_game_new_game (priv->game, &seed);
+      rand = g_rand_new_with_seed (seed);
+
+      aisleriot_game_new_game_with_rand (priv->game, rand /* adopts */);
 
       gtk_widget_grab_focus (GTK_WIDGET (priv->board));
     }
@@ -736,9 +739,7 @@ static void
 debug_choose_seed_cb (GtkAction *action,
                       AisleriotWindow *window)
 {
-  AisleriotWindowPrivate *priv = window->priv;
   GtkWidget *dialog, *entry;
-  char str[32];
 
   dialog = gtk_message_dialog_new (GTK_WINDOW (window),
                                    GTK_DIALOG_DESTROY_WITH_PARENT |
@@ -750,9 +751,16 @@ debug_choose_seed_cb (GtkAction *action,
                     G_CALLBACK (debug_choose_seed_response_cb), window);
   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
 
-  g_snprintf (str, sizeof (str), "%u", aisleriot_game_get_seed (priv->game));
   entry = gtk_entry_new ();
+
+  #if 0
+{
+  char str[32];
+  g_snprintf (str, sizeof (str), "%u", aisleriot_game_get_seed (priv->game));
   gtk_entry_set_text (GTK_ENTRY (entry), str);
+}
+#endif
+
   gtk_box_pack_end (GTK_BOX (gtk_message_dialog_get_message_area (GTK_MESSAGE_DIALOG (dialog))), entry, FALSE, FALSE, 0);
   gtk_widget_show (entry);
   g_object_set_data (G_OBJECT (dialog), "entry", entry);
@@ -1057,7 +1065,7 @@ option_cb (GtkToggleAction *action,
   aisleriot_conf_set_options (aisleriot_game_get_game_file (priv->game), (int) value);
 
   /* Now re-deal, so the option is applied */
-  aisleriot_game_new_game (priv->game, NULL);
+  aisleriot_game_new_game (priv->game);
 }
 
 static void
@@ -1210,7 +1218,7 @@ recent_game_cb (GtkAction *action,
   game_file = g_object_get_data (G_OBJECT (action), "game");
   g_return_if_fail (game_file != NULL);
 
-  aisleriot_window_set_game (window, game_file, 0);
+  aisleriot_window_set_game (window, game_file, NULL);
   
   ar_conf_set_string (NULL, aisleriot_conf_get_key (CONF_VARIATION), game_file);
 }
@@ -1696,7 +1704,7 @@ game_exception_response_cb (GtkWidget *dialog,
   gtk_widget_destroy (dialog);
 
   /* Start a new game */
-  aisleriot_game_new_game (priv->game, NULL);
+  aisleriot_game_new_game (priv->game);
 
   gtk_widget_grab_focus (GTK_WIDGET (priv->board));
 }
@@ -2465,7 +2473,7 @@ aisleriot_window_new (gboolean freecell_mode)
 typedef struct {
   AisleriotWindow *window;
   char *game_file;
-  guint seed;
+  GRand *rand;
 } LoadIdleData;
 
 static void
@@ -2474,7 +2482,7 @@ load_error_response_cb (GtkWidget *dialog,
                         AisleriotWindow *window)
 {
   /* Load the default game */
-  aisleriot_window_set_game (window, DEFAULT_VARIATION, 0);
+  aisleriot_window_set_game (window, DEFAULT_VARIATION, NULL);
 
   gtk_widget_destroy (dialog);
 }
@@ -2484,6 +2492,7 @@ load_idle_cb (LoadIdleData *data)
 {
   AisleriotWindowPrivate *priv = data->window->priv;
   GError *error = NULL;
+  GRand *rand;
 
   if (!aisleriot_game_load_game (priv->game, data->game_file, &error)) {
     GtkWidget *dialog;
@@ -2534,7 +2543,10 @@ load_idle_cb (LoadIdleData *data)
     ar_conf_set_string (NULL, aisleriot_conf_get_key (CONF_VARIATION), data->game_file);
   }
 
-  aisleriot_game_new_game (priv->game, data->seed != 0 ? &data->seed : NULL);
+  rand = data->rand;
+  data->rand = NULL;
+
+  aisleriot_game_new_game_with_rand (priv->game, rand /* adopted */);
 
   gtk_widget_grab_focus (GTK_WIDGET (priv->board));
 
@@ -2546,6 +2558,9 @@ free_load_idle_data (LoadIdleData *data)
 {
   data->window->priv->load_idle_id = 0;
 
+  if (data->rand)
+    g_rand_free (data->rand);
+
   g_free (data->game_file);
   g_slice_free (LoadIdleData, data);
 }
@@ -2554,7 +2569,7 @@ free_load_idle_data (LoadIdleData *data)
  * aisleriot_window_set_game:
  * @window:
  * @game_file: a UTF-8 string
- * @seed:
+ * @rand: (allow-none) (transfer full): a #GRand, or %NULL
  *
  * Loads the game variation defined in the @game_file file.
  * Note that even though @game_file is used as a filename,
@@ -2563,7 +2578,7 @@ free_load_idle_data (LoadIdleData *data)
 void
 aisleriot_window_set_game (AisleriotWindow *window,
                            const char *game_file,
-                           guint seed)
+                           GRand *rand)
 {
   AisleriotWindowPrivate *priv = window->priv;
   LoadIdleData *data;
@@ -2576,7 +2591,7 @@ aisleriot_window_set_game (AisleriotWindow *window,
   data = g_slice_new (LoadIdleData);
   data->window = window;
   data->game_file = g_strdup (game_file);
-  data->seed = seed;
+  data->rand = rand; /* adopted */
 
   priv->load_idle_id = g_idle_add_full (G_PRIORITY_LOW,
                                         (GSourceFunc) load_idle_cb,
diff --git a/src/window.h b/src/window.h
index 6922127..b3456cd 100644
--- a/src/window.h
+++ b/src/window.h
@@ -48,7 +48,8 @@ GType aisleriot_window_get_type (void);
 GtkWidget *aisleriot_window_new (gboolean freecell_mode);
 
 void aisleriot_window_set_game (AisleriotWindow * window,
-                                const char *game_file, guint seed);
+                                const char *game_file,
+                                GRand *rand);
 
 AisleriotGame *aisleriot_window_get_game (AisleriotWindow * window);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]