gnugo-devel
[Top][All Lists]
Advanced

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

Re: [gnugo-devel] twogtp_1_32.1


From: Gunnar Farneback
Subject: Re: [gnugo-devel] twogtp_1_32.1
Date: Wed, 10 Apr 2002 18:25:30 +0200
User-agent: EMH/1.14.1 SEMI/1.14.3 (Ushinoya) FLIM/1.14.2 (Yagi-Nishiguchi) APEL/10.3 Emacs/20.7 (sparc-sun-solaris2.7) (with unibyte mode)

Dan wrote:
> The attached patch helps make twogtp properly deterministic. The
> changes to twogtp itself add the random seeds to the sgf
> file. They also make sure a new random seed is generated for
> every game. The new seed is generated by calling the random
> number generator, then the random number generator is restarted.
> (The alternative would be to consult the time to get a new seed.)

Consulting time to get a new seed would not be a good solution. It
makes it difficult to reproduce a behaviour which depends on multiple
games (e.g. persistent cache effects, improperly initialized data
structures).

> As a consequence if we get a crash, we can copy the
> random seeds from the sgf file into the command line:
> 
> twogtp --black "gnugo --mode gtp --quiet -r 1119124199" --white "gnugo --mode 
> gtp --quiet -r 1577886685" --games 2 --boardsize 9 --sgffile tmpa.sgf
> 
> The first game will then be the game of the crash.

This doesn't seem to take multigame dependencies into account.

> The reason for this failure is that initializing the hashing
> makes calls to the random number generator, putting us at a
> different point in the sequence of pseudorandom numbers. 

This analysis is wrong. In fact the random numbers used in hash.c has
no effect on the random number sequence what so ever, thanks to this
construction: 

  /* Since the hash initialization consumes a varying number of random
   * numbers depending on the size of the Hashvalue type, we save the
   * state of the random generator now and restore it afterwards.
   */
  gg_get_rand_state(&state);
  
  [...]

  gg_set_rand_state(&state);

The place that does consume random numbers is clear_move_reasons(),
called from reset_engine().

> (1) We still get different moves from play_gmp.

Can you clarify this? What is different from what?

> (2) After this patch, twogtp no longer works with GNU Go 3.0.

That's a serious drawback and neither would it work with most other
engines. Actually I don't like this solution conceptually at all. I
suggest the following alternative approach:

1. The random number generator is reseeded at the start of
reset_engine(), using the current value of random_seed. This generally
improves reproducability of moves, as discussed earlier on the list.

2. To avoid playing the same game over and over again with current
versions of twogtp, random_seed is updated in gtp_boardsize() when
called for a non-empty board.

3. A similar update of random_seed is done in play_ascii(). (Are there
any more interfaces allowing multiple games?)

4. No update of random seed in twogtp.

The patch below makes the necessary changes in GNU Go itself and
replaces the play_gtp.c part of Dan's patch. Modifying twogtp is
beyond my abilities.

/Gunnar

Index: engine/genmove.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/genmove.c,v
retrieving revision 1.36
diff -u -r1.36 genmove.c
--- engine/genmove.c    7 Apr 2002 15:27:35 -0000       1.36
+++ engine/genmove.c    10 Apr 2002 16:13:10 -0000
@@ -67,6 +65,14 @@
 void
 reset_engine()
 {
+  /* To improve the reproducability of games, we restart the random
+   * number generator with the same seed for each move. Thus we don't
+   * have to know how many previous moves have been played, nor
+   * actually play through them, in order to get the right random
+   * numbers.
+   */
+  gg_srand(random_seed);
+
   /* Initialize things for hashing of positions. */
   reading_cache_clear();
   hashdata_recalc(&hashdata, board, board_ko_pos);
Index: engine/liberty.h
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/liberty.h,v
retrieving revision 1.94
diff -u -r1.94 liberty.h
--- engine/liberty.h    30 Mar 2002 20:29:22 -0000      1.94
+++ engine/liberty.h    10 Apr 2002 16:13:14 -0000
@@ -165,6 +165,8 @@
 void start_timer(int n);
 double time_report(int n, const char *occupation, int move, double mintime);
 
+void update_random_seed(void);
+
 
 /* Play at (pos) and then count the liberties. */
 int accurate_approxlib(int pos, int color, int maxlib, int *libs);
Index: engine/move_reasons.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/move_reasons.c,v
retrieving revision 1.78
diff -u -r1.78 move_reasons.c
--- engine/move_reasons.c       7 Apr 2002 12:38:44 -0000       1.78
+++ engine/move_reasons.c       10 Apr 2002 16:13:15 -0000
@@ -103,24 +103,6 @@
   next_all = 0;
   next_eye = 0;
   next_lunch = 0;
-
-  /* To improve the reproducability of games, we restart the random
-   * number generator with the same seed for each move. Thus we don't
-   * have to know how many previous moves have been played, nor
-   * actually play through them, in order to get the right random
-   * numbers.
-   *
-   * Comment: This means we might set these numbers only once instead
-   *          of before each move. This is not without complications
-   *          though, since we don't have full control of when the
-   *          random seed is changed. Better to do it for each move.
-   *
-   * Comment 2: While this is a good idea, we're not yet quite ready
-   *            to make use of it.
-   */
-#if 0
-  gg_srand(random_seed);
-#endif
   
   for (i = 0; i < board_size; i++)
     for (j = 0; j < board_size; j++) {
Index: engine/utils.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/engine/utils.c,v
retrieving revision 1.42
diff -u -r1.42 utils.c
--- engine/utils.c      20 Mar 2002 17:20:13 -0000      1.42
+++ engine/utils.c      10 Apr 2002 16:13:22 -0000
@@ -1740,6 +1740,18 @@
   return dt;
 }
 
+/* Update the random seed with the current value in the random sequence. */
+void
+update_random_seed(void)
+{
+  random_seed = gg_rand();
+  /* Since random seed 0 has a special interpretation when given as
+   * command line argument with the -r option, we make sure to avoid
+   * it.
+   */
+  if (random_seed == 0)
+    random_seed = 1;
+}
 
 
 /*
Index: interface/play_ascii.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/interface/play_ascii.c,v
retrieving revision 1.16
diff -u -r1.16 play_ascii.c
--- interface/play_ascii.c      31 Mar 2002 18:48:08 -0000      1.16
+++ interface/play_ascii.c      10 Apr 2002 16:13:28 -0000
@@ -988,6 +988,8 @@
     passes = 0;
     showdead = 0;
     sgf_initialized = 0;
+    /* Play a different game next time. */
+    update_random_seed();
   }
   printf("\nThanks for playing GNU Go.\n\n");
 }
Index: interface/play_gtp.c
===================================================================
RCS file: /cvsroot/gnugo/gnugo/interface/play_gtp.c,v
retrieving revision 1.70
diff -u -r1.70 play_gtp.c
--- interface/play_gtp.c        7 Apr 2002 17:00:31 -0000       1.70
+++ interface/play_gtp.c        10 Apr 2002 16:13:30 -0000
@@ -347,12 +346,24 @@
 gtp_set_boardsize(char *s, int id)
 {
   int boardsize;
+  int pos;
+  
   if (sscanf(s, "%d", &boardsize) < 1)
     return gtp_failure(id, "boardsize not an integer");
   
   if (boardsize < MIN_BOARD || boardsize > MAX_BOARD)
     return gtp_failure(id, "unacceptable boardsize");
 
+  /* If this is called with a non-empty board, we assume that a new
+   * game will be started, for which we want a new random seed.
+   */
+  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
+    if (ON_BOARD(pos) && board[pos] != EMPTY) {
+      update_random_seed();
+      break;
+    }
+  }
+  
   board_size = boardsize;
   clear_board();
   gtp_internal_set_boardsize(boardsize);



reply via email to

[Prev in Thread] Current Thread [Next in Thread]