gnugo-devel
[Top][All Lists]
Advanced

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

[gnugo-devel] Emacs mode


From: bump
Subject: [gnugo-devel] Emacs mode
Date: Wed, 19 Jan 2005 15:53:13 -0800

Revision of gnugo.el.

- Grid display implemented
- SGF handling improved
- Undo and Redo related enhancements
- Primitive edit mode
- Regression view mode
- Doc revision

All of these changes have been announced before, though
this patch fixes a few bugs. I am now adding them to the CVS
to go in gnugo-3.7.2.

Although this is a fork from Thi's version, I will attempt
to keep up with his versions if it seems possible to do that.
I have not attempted to merge with his gnugo.el-2.2.9
since it is mainly a change to gnugo-propertize-board-buffer,
which was rewritten in implementing the grid display.

Dan

Index: doc/using.texi
===================================================================
RCS file: /cvsroot/gnugo/gnugo/doc/using.texi,v
retrieving revision 1.26
diff -u -r1.26 using.texi
--- doc/using.texi      16 Nov 2004 19:41:58 -0000      1.26
+++ doc/using.texi      19 Jan 2005 23:49:57 -0000
@@ -209,6 +209,10 @@
 @noindent
 in your @file{.emacs} file. 
 
+The @file{.xpm} bitmaps for the default size Go stones are 30 
+pixels. For a larger board, alternative 36 pixel stones
+may be found in @file{gnugo-big-xpms.el}.
+
 You may start GNU Go by @command{M-x gnugo}. You will be
 prompted for command line options (@pxref{Invoking GNU
 Go}). Using these, you may set the handicap, board size,
@@ -216,132 +220,121 @@
 give a nine-stone handicap, use the options
 @option{--handicap 9 --color white}.
 
-You will immediately see an ascii board. However (unless you
-are using the alternate version @file{gnugo.el-ascii}, or
-if you are not running Emacs 21.x or later) you can get
-switch to a graphical Go board by typing `i'. You can also
-get help at any time by typing `?'.
+By default, Emacs gives you a graphical Go board. You can
+toggle an alternative ascii board (for example, if you
+want to paste a diagram into an email) with
address@hidden, which is bound to
address@hidden If you want a grid, @samp{g} toggles the grid
+display on or off. The grid is not displayed by
+default.
 
 You play a move either by moving to the location with the
 arrow keys, then hitting the SPACE key, or by clicking on
-the location with the mouse. You can save or load a game,
-and undo moves. You can get an estimate of the score at
-any time by typing @samp{!}.
-
-You can also enter commands directly to the GTP engine.
-
-Although a grid is not displayed you can get the board
-position with a grid by entering @command{: showboard RET}.
-This will enter the GTP @command{showboard} command to
-draw an ascii map of the board, which includes a grid.
-
-The file @file{gnugo.el} is under active development.
-A more recent version may work better or have more
-features. The version distributed with GNU Go 3.6
-corresponds to @file{gnugo.el-2.2.8} at
address@hidden://www.glug.org/people/ttn/software/ttn-pers-elisp/standalone/}.
-You can look there or in the GNU Go CVS for a more current version.
+an empty location with the mouse. You can save or load a game,
+and undo moves.
 
address@hidden @bullet
address@hidden
address@hidden
address@hidden Gets help.
address@hidden quotation
address@hidden  @code{RET} or @code{SPC}    
address@hidden
-Enters a move at the current position of the cursor,
-or mouse cursor in the graphical mode.
address@hidden quotation
address@hidden @samp{q} or @samp{Q}        
address@hidden
-Quit (the latter without confirmation).
address@hidden quotation
address@hidden @samp{R}             
address@hidden
-Resign.
address@hidden quotation
address@hidden             
address@hidden
-Undo one two moves. (Yours and your opponents.)
address@hidden quotation
address@hidden          
address@hidden
-Undo back to the location of the mouse cursor. After a
-move this is the last move just played, and (since you
-cannot enter this command until the computer has replied)
-the default behavior is to undo two moves. Alternatively,
-you can move the cursor to the move you want to undo
-before entering 'U', or use the prefix @command{C-} to
-enter the number of moves you want to undo. Then all
-moves are undone back to the move you selected.
address@hidden quotation
address@hidden
address@hidden
-Redraw the board.
address@hidden quotation
address@hidden or @command{M-_}
address@hidden
-Bury the Board buffer.
address@hidden quotation
address@hidden
address@hidden
-Pass.
address@hidden quotation
address@hidden
address@hidden
-Toggle display using XPM images (if supported).
address@hidden quotation
address@hidden
address@hidden
-Run `gnugo-worm-stones'.
address@hidden quotation
address@hidden
address@hidden
-Run `gnugo-dragon-stones'.
address@hidden quotation
address@hidden
address@hidden
-Run `gnugo-worm-data'.
address@hidden quotation
address@hidden
address@hidden
-Run `gnugo-dragon-data'.
address@hidden quotation
address@hidden
address@hidden
-Run `gnugo-toggle-dead-group'.
address@hidden quotation
address@hidden
address@hidden
-Get GNU Go's score estimate.
address@hidden quotation
address@hidden:} or @samp{;}
address@hidden
-Enter a command directly to the GTP engine.
address@hidden quotation
address@hidden
address@hidden
-Display board position at the cursor point.
address@hidden quotation
address@hidden
address@hidden
-Display in the echo area \"(N moves)\" followed by the
-move history, most recent move first.  This line is
-subsequently available in the *Messages* buffer.
address@hidden quotation        
address@hidden
address@hidden
-Run `gnugo-display-final-score'.
address@hidden quotation
address@hidden or @command{C-x C-w} or @command{C-x C-s}
address@hidden
-Run `gnugo-write-sgf-file'.
address@hidden quotation
address@hidden
address@hidden
-Run `gnugo-read-sgf-file'."
address@hidden quotation
address@hidden itemize
+You can get help at any time by typing @samp{?}. This will
+give a description of the default keybindings. If you want
+to find out what a particular function does, you can use
+`C-h f <function-name>' to get documentation on it. For example,
+after examining the default keybindings with @samp{?} we
+learn that @samp{v} is bound to @code{gnugo-view-regression}.
+To find out more information about this function we type
+`C-h f gnugo-view-regression' to view the help string for
+the function.
+
+You may save the game you are playing as an sgf file
+with @command{gnugo-write-sgf-file}, which is bound to
address@hidden You may also restore a saved game with
address@hidden, bound to @samp{l}.
+When the sgf file is loaded, it is assumed to be your
+move, since typically the game is saved on your move.
+You may resume play by entering a move.
+
+At the end of the game, after both players pass, GNU
+Go will run @command{gnugo-venerate} to render all
+dead stones as ghostly shades. You can then type
address@hidden to run @command{gnugo-display-final-score},
+which will tell you the score. (You may get a
+score estimate at any time before the end of the
+game with @command{gnugo-estimate-score}, bound
+to @samp{!}.
+
+You may undo your move with @command{gnugo-undo-two-moves},
+which is bound to @samp{u}. This takes back your move,
+and also the last computer move, so it goes back to
+the position two moves ago. If you undo one or many
+moves, you may redo them with @command{gnugo-redo-two-moves},
+which is bound to @samp{r}.
+
+Although if you are playing a game it is most
+natural to undo or redo two moves at a time, since
+this does not change the color of the player to
+move, you may also undo or redo a single move with
address@hidden and @command{gnugo-redo},
+bound to @samp{b} and @samp{f}. This is convenient
+for scrolling forward or backward in a game to
+review the moves. Note that if you undo once,
+then play a move (by clicking on the board, or by
+hitting the space or enter key), you have changed
+the color of the player to move. GNU Go will
+begin to generate moves as soon as you play.
+
+You may also use @command{gnugo-jump-to-move}, bound
+to @samp{j} to jump to a particular move in the
+game. You will be prompted for the game move. After
+you type the number of the move, Emacs will undo
+back to that move number. You may then redo or
+further undo using @samp{f} and @samp{f}. You may
+also jump to the beginning or end of the game with
address@hidden<} and @samp{>}.
+
+Another way to undo back to a given move is to move
+the cursor to a stone (which must be one of your
+own), then execute @command{gnugo-magic-undo},
+bound to @samp{U}.
+
+As we have noted, GNU Go normally answers each move
+that you play by generating a move of its own.  If
+you want to suppress GNU Go's automatic generation
+of moves, you may toggle an `editing mode' with
address@hidden In the editing
+mode, GNU Go does not automatically answer each
+move that you play. For example, you can use the
+editing mode to write an sgf file from scratch.
+If you are playing a game, you can turn off
+GNU Go's automatic responses, play a few moves
+in editing mode to see what the board position
+will look like, then back up to the last move,
+toggle the editing mode off, then resume the game.
+
+You may view a GNU Go regression test with 
address@hidden, which will prompt you
+for the name of a test. You may type (for example)
address@hidden:6}. The first time you do this
+you will be prompted for the path to the
address@hidden/} directory. (Once Emacs knows this
+path, you will not be prompted again.) This command
+takes a while to execute since GNU Go will run the
+regression. When it is completed, Emacs will display
+the board position (with the grid) and a message
+below the board such as:
address@hidden
+ loadsgf games/incident104.sgf 63
+ strategy:6 reg_genmove white
+ #? [E10]*
+ =6 J13
address@hidden example
+This gives the actual test followed by the move that GNU Go
+generates when running the test.
+
+You may also ask GNU Go to identify a dragon on the
+board. Click on one stone to move the cursor to that
+location. Then type @samp{d}. The dragon in question
+will then be marked flashing. You may also type
address@hidden, which will report the dragon data. You
+may run other gtp commands with @command{gnugo-command},
+which is bound to @samp{:}.
 
 @node GMP and GTP
 @section The Go Modem Protocol and Go Text Protocol
Index: interface/gnugo.el
===================================================================
RCS file: /cvsroot/gnugo/gnugo/interface/gnugo.el,v
retrieving revision 1.10
diff -u -r1.10 gnugo.el
--- interface/gnugo.el  16 Nov 2004 19:41:58 -0000      1.10
+++ interface/gnugo.el  19 Jan 2005 23:49:57 -0000
@@ -169,10 +169,21 @@
 (require 'cl)                           ; use the source luke!
 (ignore-errors (require 'time-date))    ; for `time-subtract'
 
+
+;;; ==========================================================================
+
+; Modifications to gnugo.el-2.2.8:
+;
+; * Grid display implemented
+; * SGF handling improved
+; * Undo and Redo related enhancements
+; * Primitive edit mode
+; * Regression view mode
+
 ;;;---------------------------------------------------------------------------
 ;;; Political arts
 
-(defconst gnugo-version "2.2.8"
+(defconst gnugo-version "2.2.8.b5"
   "Version of gnugo.el currently loaded.
 Note that more than two dots in the value indicates \"pre-release\",
 or \"alpha\" or \"hackers-invited-all-else-beware\"; use at your own risk!
@@ -227,12 +238,14 @@
 character in the string, then the next, and so on until the string (and/or
 the viewer) is exhausted.")
 
-(defvar gnugo-mode-line "~b ~w :~u"
+(defvar gnugo-mode-line "~b ~w :~m ~n :~u"
   "*A `mode-line-format'-compliant value for GNUGO Board mode.
 If a single string, the following special escape sequences are
 replaced with their associated information:
   ~b,~w  black,white captures (a number)
   ~p     current player (black or white)
+  ~m     move number
+  ~n     size of undo stack
   ~t     time waiting for the current move
   ~u     time taken for the Ultimate (most recent) move
 The times are in seconds, or \"-\" if that information is not available.
@@ -258,6 +271,8 @@
                          ;                    gnugo-state)
                          ;           (reverse acc))))))
 
+(defvar gnugo-regression-directory nil)
+
 (eval-when-compile
   (defvar gnugo-xpms nil))
 
@@ -317,6 +332,7 @@
  :sgf-tree -- the (very simple) list of nodes, each node a list of
               properties of the form `(:XY . VALUE)'; see functions
               `gnugo-push-move', `gnugo-note' and `gnugo-write-sgf-file'
+ :future-history -- an undo stack (so moves undone may be redone)
 
  :gnugo-color -- either \"black\" or \"white\"
  :user-color
@@ -334,6 +350,7 @@
  :display-using-images -- XPMs, to be precise; see functions `gnugo-yy',
                           `gnugo-toggle-image-display' and `gnugo-refresh',
                           as well as gnugo-xpms.el (available elsewhere)
+ :show-grid -- display the grid
 
  :all-yy -- list of 46 keywords used as the `category' text property
             (so that their plists, typically w/ property `display' or
@@ -424,16 +441,17 @@
 
 (defun gnugo-goto-pos (pos)
   "Move point to board position POS, a letter-number string."
-  (goto-char (point-min))
-  (forward-line (- (1+ (gnugo-get :board-size))
-                   (string-to-number (substring pos 1))))
-  (forward-char 1)
-  (forward-char (+ (if (= 32 (following-char)) 1 2)
-                   (* 2 (- (let ((letter (aref pos 0)))
-                             (if (> ?I letter)
-                                 letter
-                               (1- letter)))
-                           ?A)))))
+  (unless (string= pos "PASS")
+    (goto-char (point-min))
+    (forward-line (- (+ 2 (gnugo-get :board-size))
+                    (string-to-number (substring pos 1))))
+    (forward-char 2)
+    (forward-char (+ (if (= 32 (following-char)) 1 2)
+                    (* 2 (- (let ((letter (aref pos 0)))
+                              (if (> ?I letter)
+                                  letter
+                                (1- letter)))
+                            ?A))))))
 
 (defun gnugo-f (frag)
   (intern (format ":gnugo-%s%s-props" (gnugo-get :diamond) frag)))
@@ -482,6 +500,8 @@
                                    ;; `(display (space :width 0))'
                                    ;; works as well, for newer emacs
                                    '(invisible t)))
+    (setplist (gnugo-f 'jspc)
+             (and new `(display (space :width ,(- (gnugo-get :w-imul) 1)))))
     (gnugo-put :highlight-last-move-spec
       (if new
           '((lambda (p)
@@ -499,22 +519,52 @@
     (gnugo-put :hmul (if new (gnugo-get :h-imul) 1))
     (gnugo-put :display-using-images new)))
 
+(defun gnugo-toggle-grid ()
+  "Turn the grid around the board on or off."
+  (interactive)
+  (gnugo-put :show-grid (not (gnugo-get :show-grid)))
+  (gnugo-refresh t))
+
+(defun gnugo-propertize-grid-line (size)
+  (put-text-property (point) (+ 1 (point)) 
+                    'category (gnugo-f 'lpad))
+  (do ((p (+ 4 (point)) (+ 2 p)) (ival 'even (if (eq 'even ival) 'odd 'even)))
+      ((< (+ (* 2 size) 3 (point)) p))
+    (add-text-properties p (1+ p)
+                        `(gnugo-yin
+                          ,5
+                          gnugo-yang
+                          ,'empty
+                          front-sticky
+                          (gnugo-position gnugo-yin)))
+    (add-text-properties (- p 1) p
+                        `(category
+                          ,(gnugo-f 'jspc)
+                          rear-nonsticky
+                          t))
+    (put-text-property (- p 2) p 'intangible ival)))
+
 (defun gnugo-propertize-board-buffer ()
   (erase-buffer)
   (insert (substring (cdr (gnugo-synchronous-send/return "showboard")) 3))
   (let* ((size (gnugo-get :board-size))
          (size-string (number-to-string size)))
-    (goto-char (point-min))
-    (put-text-property (point) (1+ (point)) 'category (gnugo-f 'tpad))
+    (beginning-of-buffer)
+    (insert " \n")
+    (put-text-property (point-min) (+ 1 (point-min)) 'category (gnugo-f 'tpad))
+    (insert " ")
+    (beginning-of-line)
+    (gnugo-propertize-grid-line size)
     (forward-line 1)
-    (put-text-property (point-min) (point) 'invisible t)
+    (insert " ")
+    (beginning-of-line)
     (while (looking-at "\\s-*\\([0-9]+\\)[ ]")
       (let* ((row (match-string-no-properties 1))
              (edge (match-end 0))
              (other-edge (+ edge (* 2 size) -1))
              (top-p (string= size-string row))
              (bot-p (string= "1" row)))
-        (put-text-property (point) (1- edge) 'category (gnugo-f 'lpad))
+        (put-text-property (point) (1+ (point)) 'category (gnugo-f 'lpad))
         (do ((p edge (+ 2 p)) (ival 'even (if (eq 'even ival) 'odd 'even)))
             ((< other-edge p))
           (let* ((position (format "%c%s" (aref [?A ?B ?C ?D ?E ?F ?G ?H
@@ -554,15 +604,27 @@
             (put-text-property p (+ 2 p) 'intangible ival)))
         (goto-char (+ other-edge (length row) 1))
         (when (looking-at "\\s-+\\(WH\\|BL\\).*capt.* \\([0-9]+\\).*$")
-          (let ((prop (if (string= "WH" (match-string 1))
-                          :white-captures
-                        :black-captures)))
-            (put-text-property (match-beginning 2) (match-end 2) 'field prop)
-            (gnugo-put prop (match-string-no-properties 2))))
+         (kill-line))
+       (unless (gnugo-get :show-grid)
+           (save-excursion
+             (put-text-property (line-beginning-position)
+                                (+ 3 (line-beginning-position))
+                                'invisible t)
+             (put-text-property (+ 3 (* 2 size) (line-beginning-position))
+                                (line-end-position)
+                                'invisible t)
+             (beginning-of-buffer)
+             (forward-line 1)
+             (put-text-property (point) (line-end-position) 'invisible t)
+             (end-of-buffer)
+             (put-text-property 
+              (line-beginning-position) (point) 'invisible t)))
         (end-of-line)
-        (put-text-property other-edge (point) 'category (gnugo-f 'rpad))
-        (forward-char 1)))
-    (put-text-property (1- (point)) (point-max) 'invisible t)))
+        ;(put-text-property other-edge (point) 'category (gnugo-f 'rpad))
+        (forward-char 1)
+       (insert " ")
+       (beginning-of-line)))
+      (gnugo-propertize-grid-line size)))
 
 (defun gnugo-merge-showboard-results ()
   (let ((aft (substring (cdr (gnugo-synchronous-send/return "showboard")) 3))
@@ -620,6 +682,27 @@
         (when (setq very-strange (get-text-property (1+ cut) 'intangible))
           (put-text-property cut (1+ cut) 'intangible very-strange))))))
 
+(defun gnugo-sgf-to-gtp (cc) 
+  "Convert board locations from the format used by sgf to the format used by 
gtp."
+  (interactive)
+  (if (string= "tt" cc)
+      "PASS"
+    (let ((col (aref cc 0)))
+      (format "%c%d"
+             (+ ?A (- (if (> ?i col) col (1+ col)) ?a))
+             (- (gnugo-get :board-size) (- (aref cc 1) ?a))))))
+
+(defun gnugo-gtp-to-sgf (value)
+  "Convert board locations from the format used by gtp to the format used by 
sgf."
+  (interactive)
+  (if (string= "PASS" value)
+      "tt"
+    (let* ((col (aref value 0))
+          (one (+ ?a (- (if (< ?H col) (1- col) col) ?A)))
+          (two (+ ?a (- (gnugo-get :board-size) 
+                        (string-to-number (substring value 1))))))
+      (format "%c%c" one two))))
+
 (defun gnugo-move-history (&optional rsel)
   "Determine and return the game's move history.
 Optional arg RSEL controls side effects and return value.
@@ -705,11 +788,12 @@
          (head (gnugo-move-history 'car))
          (onep (and head (string= "PASS" head)))
          (donep (or resignp (and onep passp))))
-    (unless passp
-      (gnugo-merge-showboard-results))
+;    (unless passp
+;      (gnugo-merge-showboard-results))
     (gnugo-put :last-mover color)
     (when userp
       (gnugo-put :last-user-bpos (and (not passp) (not resignp) move)))
+    (gnugo-put :future-history nil)
     (gnugo-note (if (string= "black" color) :B :W) move t (not resignp))
     (when resignp
       (gnugo-note :EV "resignation"))
@@ -752,8 +836,20 @@
             `((live ,@live)
               (dead ,@dead))))))
     (gnugo-put :waiting-start (and (not donep) now))
+    (gnugo-put :black-captures (gnugo-query "captures black"))
+    (gnugo-put :white-captures (gnugo-query "captures white"))
+    (gnugo-refresh t)
     donep))
 
+(defun gnugo-toggle-edit-mode ()
+  "Toggle :edit-mode. When true, GNU Go is not called to generate moves."
+  (interactive)
+  (gnugo-put :edit-mode (not (gnugo-get :edit-mode)))
+  (if (gnugo-get :edit-mode)
+      (setq mode-name "Editing SGF File")
+    (setq mode-name "Playing GNU Go"))
+  (gnugo-refresh))
+
 (defun gnugo-venerate (yin yang)
   (let* ((fg-yy (gnugo-yy yin yang))
          (fg-disp (or (get fg-yy 'display)
@@ -878,7 +974,7 @@
              (h (ash (- (window-height window)
                         (round (* size (gnugo-get :hmul)))
                         1)
-                     -1))
+                     -5))
              (edges (window-edges window))
              (right-w-edge (nth 2 edges))
              (avail-width (- right-w-edge (nth 0 edges)))
@@ -887,7 +983,7 @@
                          (if (symbol-plist (gnugo-f 'ispc))
                              0
                            (1- size)))
-                      2)
+                      8)
                    2.0)))
         (dolist (pair `((tpad . ,(if (and h (< 0 h))
                                      `(display ,(make-string h 10))
@@ -907,7 +1003,7 @@
           (cond ((stringp cur)
                  (setq cur (copy-sequence cur))
                  (let (acc cut c)
-                   (while (setq cut (string-match "~[bwptu]" cur))
+                   (while (setq cut (string-match "~[bwmnptu]" cur))
                      (aset cur cut ?%)
                      (setq cut (1+ cut) c (aref cur cut))
                      (aset cur cut ?s)
@@ -916,6 +1012,8 @@
                         ,(case c
                            (?b '(or (gnugo-get :black-captures) 0))
                            (?w '(or (gnugo-get :white-captures) 0))
+                           (?m '(length (cdr (gnugo-get :sgf-tree))))
+                           (?n '(length (gnugo-get :future-history)))
                            (?p '(gnugo-other (gnugo-get :last-mover)))
                            (?t '(let ((ws (gnugo-get :waiting-start)))
                                   (if ws
@@ -976,7 +1074,8 @@
                             tpad
                             lpad
                             rpad
-                            ispc))))
+                            ispc
+                            jspc))))
     (setq gnugo-state nil)))
 
 (defun gnugo-position ()
@@ -1002,8 +1101,12 @@
       (unless inhibit-gnugo-refresh
         (with-current-buffer buf
           (gnugo-refresh))))
-    (with-current-buffer buf
-      (gnugo-get-move (gnugo-get :gnugo-color)))))
+    (if (not (gnugo-get :edit-mode))
+       (with-current-buffer buf
+         (gnugo-get-move (gnugo-get :gnugo-color)))
+      (progn
+       (gnugo-put :user-color (gnugo-other (gnugo-get :user-color)))
+       (gnugo-put :gnugo-color (gnugo-other (gnugo-get :gnugo-color)))))))
 
 (defun gnugo-mouse-move (e)
   "Do `gnugo-move' at mouse location."
@@ -1208,10 +1311,11 @@
              (not (y-or-n-p "File exists. Continue? ")))
     (error "Not writing %s" filename))
   ;; todo: write sgf.el; call to it here
-  (let ((bef-newline-appreciated '(:C :B :W :PB :PW))           ;;; aesthetic
-        (aft-newline-appreciated '(:C :B :W :SZ :PB :PW))       ;;;  license
+  (let ((bef-newline-appreciated '(:C :PB :PW :AB :AW))
+        (aft-newline-appreciated '(:C :B :AB :AW :W :PB :PW :SZ))
         (sz (gnugo-get :board-size))
-        (tree (gnugo-get :sgf-tree)))
+        (tree (gnugo-get :sgf-tree))
+       newline-just-printed)
     (with-temp-buffer
       (insert "(")
       (dolist (node (reverse tree))
@@ -1219,18 +1323,153 @@
         (dolist (prop (reverse node))
           (let ((name (car prop))
                 (v (cdr prop)))
-            (insert
-             (if (memq name bef-newline-appreciated) "\n" "")
-             (substring (symbol-name name) 1)
-             "[" (format "%s" v) "]"
-             (if (memq name aft-newline-appreciated) "\n" "")))))
+           (insert
+            (if (and (memq name bef-newline-appreciated) 
+                     (not newline-just-printed)) "\n" "")
+            (substring (symbol-name name) 1)
+            (if (not (memq name '(:AB :AW))) "[" "")
+            (format "%s" v)
+            (if (not (memq name '(:AB :AW))) "]" "")
+            (if (or (memq name aft-newline-appreciated)
+                    (> (current-column) 60)) "\n" ""))
+           (setq newline-just-printed
+                 (memq name aft-newline-appreciated)))))
       (insert ")\n")
       (write-file filename))))
 
+(defun gnugo-warp-point ()
+  "Move the cursor to the next-to-last move."
+  (interactive)
+  (let ((moves (cdr (gnugo-get :sgf-tree))))
+    (if (memq (car (car (car moves))) '(:B :W))
+       (gnugo-goto-pos (gnugo-sgf-to-gtp (cdr (car (car moves))))))))
+
+(defun gnugo-initialize-sgf-tree ()
+  "Start a new sgf tree"
+  (gnugo-put :sgf-tree (list (list)))
+  (let ((g-blackp (string= "black" (gnugo-get :gnugo-color)))
+       (black-stones (split-string (gnugo-query "list_stones black") " "))
+       (white-stones (split-string (gnugo-query "list_stones white") " ")))
+    (mapc (lambda (x) (apply 'gnugo-note x))
+          `((:GM 1)
+            (:FF 4)                     ; hmm maybe better: 3
+            (:DT ,(format-time-string "%Y-%m-%d"))
+            (:RU ,(gnugo-get :rules))
+            (:HA ,(gnugo-get :handicap))
+            (:SZ ,(gnugo-get :board-size))
+            (:KM ,(gnugo-get :komi))
+            (:AP ,(format "gnugo.el:%s" gnugo-version))
+            (,(if g-blackp :PW :PB) ,(user-full-name))
+            (,(if g-blackp :PB :PW) ,(concat "GNU Go "
+                                             (gnugo-query "version")))))
+    (if black-stones
+       (gnugo-note :AB
+                   (apply 'concat
+                          (mapcar 
+                           (lambda (x) (format "[%s]" (gnugo-gtp-to-sgf x)))
+                           black-stones))))
+    (if white-stones
+       (gnugo-note :AW
+                   (apply 'concat
+                          (mapcar 
+                           (lambda (x) (format "[%s]" (gnugo-gtp-to-sgf x)))
+                           white-stones))))))
+
 (defun gnugo-read-sgf-file (filename)
   "Load a game tree from FILENAME, a file in SGF format."
   (interactive "fSGF file to load: ")
-  (gnugo-command (format "loadsgf %s" (expand-file-name filename))))
+  (gnugo-command (format "loadsgf %s 1" (expand-file-name filename)))
+  (gnugo-put :board-size 
+    (string-to-number (gnugo-query "query_boardsize")))
+  (gnugo-put :handicap 
+    (string-to-number (gnugo-query "get_handicap")))
+  (gnugo-put :komi 
+    (string-to-number (gnugo-query "get_komi")))
+  (gnugo-put :future-history nil)
+  (gnugo-initialize-sgf-tree)
+  (gnugo-command (format "loadsgf %s" (expand-file-name filename)))
+  (let* ((colorhistory 
+         (mapcar 
+          (lambda (x) (split-string x " ")) 
+          (split-string 
+           (cdr (gnugo-synchronous-send/return "move_history")) "[=\n]")))
+        (k (length colorhistory)))
+    (unless (equal colorhistory '(nil)) ; empty move history gives this
+      (gnugo-put :last-mover
+       (car (car colorhistory)))
+      (let ((half (ash (1+ (gnugo-get :board-size)) -1)))
+       (gnugo-goto-pos (format "A%d" half))
+       (forward-char (* 2 (1- half)))
+       (gnugo-put :last-user-bpos
+         (gnugo-put :center-position
+           (get-text-property (point) 'gnugo-position))))
+      (while (> k 0)
+       (decf k)
+       (gnugo-note (if (string= (car (nth k colorhistory)) "black") :B :W)
+                   (nth 1 (nth k colorhistory)) t t))))
+  (gnugo-refresh t)
+  (gnugo-warp-point))
+
+(defun gnugo-undo (&optional norefresh)
+  "Undo one move. Interchange the colors of the two players."
+  (interactive)
+  (gnugo-gate)
+  (unless (and (gnugo-get :game-over) ; engine should undo pass but not resign
+              (not
+               (string= "PASS" 
+                        (nth 1 
+                             (split-string (gnugo-query "last_move") " ")))))
+    (if (equal
+        (car
+         (split-string 
+          (cdr (gnugo-synchronous-send/return "undo")) " ")) "?")
+       (error "cannot undo")
+      (gnugo-put :future-history
+       (cons (car (gnugo-get :sgf-tree)) (gnugo-get :future-history)))))
+  (gnugo-put :sgf-tree (cdr (gnugo-get :sgf-tree)))
+  (gnugo-put :user-color (gnugo-get :last-mover))
+  (gnugo-put :gnugo-color (gnugo-other (gnugo-get :last-mover)))
+  (gnugo-put :last-mover (gnugo-get :gnugo-color))
+  (gnugo-put :game-over nil)
+; (gnugo-merge-showboard-results)
+  (unless norefresh
+    (gnugo-refresh t)
+    (gnugo-warp-point)))
+
+(defun gnugo-redo (&optional norefresh)
+  "Redo one move from the undo-stack (future-history).
+   Interchange the colors of the two players."
+  (interactive)
+  (gnugo-gate)
+  (if (equal (gnugo-get :future-history) nil)
+      (error "no more undone moves left to redo!"))
+  (let* ((buf (current-buffer))
+        (pos (gnugo-sgf-to-gtp (cdr (car (car (gnugo-get :future-history))))))
+       (color (if (equal (car (car (car (gnugo-get :future-history)))) :B) 
+                  "black" "white"))
+       (move (format "play %s %s" color pos))
+       (accept (cdr (gnugo-synchronous-send/return move))))
+    (gnugo-note (if (string= "black" color) :B :W) pos t t)
+    (gnugo-put :future-history (cdr (gnugo-get :future-history)))
+    (gnugo-put :user-color (gnugo-other color))
+    (gnugo-put :gnugo-color color)
+    (gnugo-put :last-mover color)
+;    (gnugo-merge-showboard-results)
+    (unless norefresh
+      (gnugo-refresh t)
+      (gnugo-warp-point))))
+
+(defun gnugo-redo-two-moves ()
+  "Redo a pair of moves (yours and GNU Go's).
+If two moves cannot be found, do nothing. (If there is
+exactly one move in the undo stack, you can still redo
+it using gnugo-redo.)"
+  (interactive)
+  (gnugo-gate)
+  (if (cdr (gnugo-get :future-history))
+      (gnugo-redo)
+    (error "can't redo two moves\n"))
+  (gnugo-redo))
 
 (defun gnugo-magic-undo (spec &optional noalt)
   "Undo moves on the GNUGO Board, based on SPEC, a string or number.
@@ -1253,9 +1492,9 @@
            (setq n spec done (lambda () (= 0 n))))
           ((string-match "^[a-z]" spec)
            (let ((pos (upcase spec)))
-             (setq done `(lambda ()
-                           (gnugo-goto-pos ,pos)
-                           (memq (char-after) '(?. ?+))))
+             (setq done `(lambda () 
+                          (equal 
+                           (gnugo-query ,(concat "color " pos)) "empty")))
              (when (funcall done)
                (error "%s already clear" pos))
              (let ((u (gnugo-get :user-color)))
@@ -1267,21 +1506,24 @@
                           ?X))
                  (error "%s not occupied by %s" pos u)))))
           (t (error "bad spec: %S" spec)))
-    (when (gnugo-get :game-over)
-      ;; fixme: clean up :sgf-tree here.
-      (gnugo-put :game-over nil))
     (while (not (funcall done))
-      (setq ans (cdr (gnugo-synchronous-send/return "undo")))
-      (unless (= ?= (aref ans 0))
-        (error ans))
+      (if (gnugo-get :game-over)
+         (gnugo-put :game-over nil)
+       (progn
+         (setq ans (cdr (gnugo-synchronous-send/return "undo")))
+         (unless (= ?= (aref ans 0))
+           (gnugo-refresh t)
+           (error ans))
+         (gnugo-put :future-history
+           (cons (car (gnugo-get :sgf-tree)) (gnugo-get :future-history)))))
       (gnugo-put :sgf-tree (cdr (gnugo-get :sgf-tree)))
       (gnugo-put :last-mover (gnugo-other (gnugo-get :last-mover)))
-      (gnugo-merge-showboard-results)   ; all
-      (gnugo-refresh)                   ; this
+;     (gnugo-merge-showboard-results)   ; all
+;     (gnugo-refresh t)                 ; this
       (decf n)                          ; is
       (sit-for 0)))                     ; eye candy
   (let* ((ulastp (string= (gnugo-get :last-mover) (gnugo-get :user-color)))
-
+        
          (ubpos (gnugo-move-history (if ulastp 'car 'cadr))))
     (gnugo-put :last-user-bpos (if (and ubpos (not (string= "PASS" ubpos)))
                                    ubpos
@@ -1309,6 +1551,100 @@
                         1
                       2)))
 
+(defun gnugo-jump-to-move (movenum)
+  "Jump to move number MOVENUM."
+  (interactive)
+  (unless 
+      (and
+       (>= movenum 0)
+       (<= movenum (+ (length (cdr (gnugo-get :sgf-tree)))
+                     (length (gnugo-get :future-history)))))
+    (error "invalid move number"))
+  (while (not (= movenum (length (cdr (gnugo-get :sgf-tree)))))
+    (if (< movenum (length (cdr (gnugo-get :sgf-tree))))
+       (gnugo-undo t)
+      (gnugo-redo t)))
+  (gnugo-refresh t)
+  (gnugo-warp-point))
+
+(defun gnugo-jump-to-beginning ()
+  "Jump to the beginning of the game."
+  (interactive)
+  (gnugo-jump-to-move 0))
+
+(defun gnugo-jump-to-end ()
+  "Jump to the end of the game"
+  (interactive)
+  (gnugo-jump-to-move (+ (length (cdr (gnugo-get :sgf-tree)))
+        (length (gnugo-get :future-history)))))
+
+(defun gnugo-get-regression-directory (filename)
+  "Prompt the user for the regression directory."
+  (interactive "fRegression directory: ")
+  (setq gnugo-regression-directory (expand-file-name filename)))
+
+(defun gnugo-view-regression (test)
+  "View one of the standard gnugo regressions.
+   Enter the name of the test in the format filename:testnumber.
+   The filename must be a file in the regression directory. The
+   first time the function is run, you will be prompted for the
+   path to that directory."
+  (interactive "sTest: ")
+  (let* ((file (car (split-string test ":")))
+       (testnumber (nth 1 (split-string test ":")))
+       (gnugo-buffer (current-buffer))
+       (file-already-open nil))
+    (unless gnugo-regression-directory
+      (call-interactively 'gnugo-get-regression-directory))
+    (unless gnugo-regression-directory
+      (error "directory not found"))
+    (let ((filename
+          (concat gnugo-regression-directory file ".tst")))
+      (if (find-buffer-visiting filename)
+         (setq file-already-open t))
+      (find-file filename))
+    (beginning-of-buffer)
+    (unless
+       (re-search-forward (concat "^" testnumber " ") nil t)
+      (unless file-already-open (kill-buffer (current-buffer)))
+      (switch-to-buffer gnugo-buffer)
+      (error "test not found"))
+    (beginning-of-line)
+    (let* ((second-line (buffer-substring
+                        (line-beginning-position)
+                        (line-end-position)))
+          (third-line (progn
+                        (forward-line)
+                        (buffer-substring
+                        (line-beginning-position)
+                        (line-end-position))))
+          (first-line (progn (re-search-backward "loadsgf")
+                             (buffer-substring
+                              (line-beginning-position)
+                              (line-end-position))))
+          (first-line-split (split-string first-line)))
+      ; don't close the file if the user was visiting it
+      (unless file-already-open (kill-buffer (current-buffer)))
+      (switch-to-buffer gnugo-buffer)
+      (gnugo-read-sgf-file
+       (concat gnugo-regression-directory (nth 1 first-line-split)))
+      (if (> (length first-line-split) 2)
+         (gnugo-jump-to-move (1- (string-to-number 
+                                  (nth 2 first-line-split)))))
+      (setq mode-name "running test ...")
+      (gnugo-put :show-grid t)
+      (gnugo-refresh t)
+      (end-of-buffer)
+      (insert "\n\n ")
+      (insert first-line)
+      (insert "\n ")
+      (insert (format "%s:%s" file second-line))
+      (insert "\n ")
+      (insert third-line)
+      (insert "\n ")
+      (setq mode-name (format "%s" test))
+      (insert (cdr (gnugo-synchronous-send/return second-line))))))
+
 (defun gnugo-display-final-score ()
   "Display final score and other info in another buffer (when game over).
 If the game is still ongoing, Emacs asks if you wish to stop play (by
@@ -1491,7 +1827,10 @@
 (defun gnugo-board-mode ()
   "Major mode for playing GNU Go.
 Entering this mode runs the normal hook `gnugo-board-mode-hook'.
-In this mode, keys do not self insert.  Default keybindings:
+In this mode, keys do not self insert. You can get further help
+describing any particular function with `C-h f <function-name>',
+for example `C-h f gnugo-move'.
+Default keybindings:
 
   ?             View this help.
 
@@ -1503,13 +1842,28 @@
 
   u             Run `gnugo-undo-two-moves'.
 
+  r             Redo two moves.
+
   U             Pass to `gnugo-magic-undo' either the board position
                 at point (if no prefix arg), or the prefix arg converted
                 to a number.  E.g., to undo 16 moves: `C-u C-u U' (see
                 `universal-argument'); to undo 42 moves: `M-4 M-2 U'.
 
-  C-l           Run `gnugo-refresh'.
+  f             Scroll forward (redo one undone move); 
+                potentially switch colors.
+
+  b             Scroll backward (undo one move); potentially switch colors.
+
+  <             Go to the beginning of the game
+
+  >             Go to the end of the game
+
+  j <n> RET     Jump to move number <n>
 
+  g             toggle the grid on or off.
+
+  C-l           Run `gnugo-refresh' to redraw the board.
+ 
   _ or M-_      Bury the Board buffer (when the boss is near).
 
   P             Run `gnugo-pass'.
@@ -1535,6 +1889,8 @@
   F             Run `gnugo-display-final-score'.
 
   s             Run `gnugo-write-sgf-file'.
+
+  v             Run `gnugo-view-regression'.
   or C-x C-w
   or C-x C-s
 
@@ -1547,7 +1903,7 @@
   (set (make-local-variable 'font-lock-defaults)
        '(gnugo-font-lock-keywords t))
   (setq major-mode 'gnugo-board-mode)
-  (setq mode-name "GNUGO Board")
+  (setq mode-name "Playing GNU Go")
   (add-hook 'kill-buffer-hook 'gnugo-cleanup nil t)
   (make-local-variable 'gnugo-state)
   (setq gnugo-state (make-hash-table :size (1- 42) :test 'eq))
@@ -1560,7 +1916,9 @@
           :white-captures
           :mode-line
           :mode-line-form
+          :edit-mode
           :display-using-images
+          :show-grid
           :xpms
           :local-xpms
           :all-yy))
@@ -1624,28 +1982,9 @@
   (gnugo-put :rparen-ov (let ((ov (make-overlay 1 1)))
                           (overlay-put ov 'display ")")
                           ov))
-  (gnugo-put :sgf-tree (list (list)))
-  (let ((g-blackp (string= "black" (gnugo-get :gnugo-color))))
-    (mapc (lambda (x) (apply 'gnugo-note x))
-          `((:GM 1)
-            (:FF 4)                     ; hmm maybe better: 3
-            (:DT ,(format-time-string "%Y-%m-%d"))
-            (:RU ,(gnugo-get :rules))
-            (:SZ ,(gnugo-get :board-size))
-            (:KM ,(gnugo-get :komi))
-            (,(if g-blackp :PW :PB) ,(user-full-name))
-            (,(if g-blackp :PB :PW) ,(concat "GNU Go "
-                                             (gnugo-query "version")))
-            ,@(let ((h (gnugo-get :handicap)))
-                (when (not (= 0 h))
-                  `((:HA ,h)
-                    ,@(mapcar
-                       ;; AB can be a list, but we stay simple so that
-                       ;; `gnugo-write-sgf-file' can also remain simple
-                       (lambda (stone)
-                         `(:AB ,stone nil t))
-                       (split-string
-                        (gnugo-query "fixed_handicap %d" h)))))))))
+  (if (< 0 (gnugo-get :handicap))
+        (gnugo-query (format "fixed_handicap %d" (gnugo-get :handicap))))
+  (gnugo-initialize-sgf-tree)
   (set-process-sentinel (gnugo-get :proc) 'gnugo-sentinel)
   (set-process-buffer (gnugo-get :proc) (current-buffer))
   (gnugo-put :waiting-start (current-time))
@@ -1701,6 +2040,10 @@
           (gnugo-put :center-position
             (get-text-property (point) 'gnugo-position))))
       ;; first move
+      (if (and (fboundp 'display-images-p) (display-images-p))
+         (progn
+           (gnugo-toggle-image-display)
+           (gnugo-refresh t)))
       (gnugo-put :game-start-time (current-time))
       (let ((g (gnugo-get :gnugo-color))
             (n (gnugo-get :handicap))
@@ -1738,6 +2081,13 @@
                                    ((consp x) (car x))
                                    (t (gnugo-position))))))
             ("u"        . gnugo-undo-two-moves)
+            ("r"        . gnugo-redo-two-moves)
+            ("f"        . gnugo-redo)
+            ("b"        . gnugo-undo)
+            ("j"        . (lambda (x) (interactive "nJump to move number: ")
+                           (gnugo-jump-to-move x)))
+            ("<"        . gnugo-jump-to-beginning)
+           (">"        . gnugo-jump-to-end)
             ("\C-l"     . gnugo-refresh)
             ("\M-_"     . bury-buffer)
             ("_"        . bury-buffer)
@@ -1745,11 +2095,14 @@
             ("i"        . (lambda () (interactive)
                             (gnugo-toggle-image-display)
                             (save-excursion (gnugo-refresh))))
+           ("e"        . gnugo-toggle-edit-mode)
             ("w"        . gnugo-worm-stones)
             ("W"        . gnugo-worm-data)
             ("d"        . gnugo-dragon-stones)
             ("D"        . gnugo-dragon-data)
             ("t"        . gnugo-toggle-dead-group)
+            ("g"        . gnugo-toggle-grid)
+            ("v"        . gnugo-view-regression)
             ("!"        . gnugo-estimate-score)
             (":"        . gnugo-command)
             (";"        . gnugo-command)
@@ -1821,7 +2174,7 @@
     (defgtp '(boardsize
               clear_board
               fixed_handicap
-              loadsgf)
+             loadsgf)
       :output :discard
       :post-hook (lambda ()
                    (dolist (prop '(:game-over





reply via email to

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