[Top][All Lists]
[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
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [gnugo-devel] Emacs mode,
bump <=