[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] master 6160089: * externals-list: Convert gnugo to :external
From: |
Stefan Monnier |
Subject: |
[elpa] master 6160089: * externals-list: Convert gnugo to :external |
Date: |
Sun, 29 Nov 2020 14:52:00 -0500 (EST) |
branch: master
commit 61600893f3f33e3c80650332e472f0a1aa66857f
Author: Stefan Monnier <monnier@iro.umontreal.ca>
Commit: Stefan Monnier <monnier@iro.umontreal.ca>
* externals-list: Convert gnugo to :external
---
externals-list | 11 +-
packages/gnugo/.dir-locals.el | 5 -
packages/gnugo/HACKING | 86 --
packages/gnugo/NEWS | 240 ----
packages/gnugo/README | 6 -
packages/gnugo/THANKS | 13 -
packages/gnugo/gnugo-frolic.el | 509 -------
packages/gnugo/gnugo-imgen.el | 258 ----
packages/gnugo/gnugo.el | 2866 ----------------------------------------
9 files changed, 6 insertions(+), 3988 deletions(-)
diff --git a/externals-list b/externals-list
index e918af2..9b2b310 100644
--- a/externals-list
+++ b/externals-list
@@ -91,7 +91,7 @@
("f90-interface-browser" :external "https://github.com/wence-/f90-iface")
("flymake" :core "lisp/progmodes/flymake.el")
("frog-menu" :external "https://github.com/clemera/frog-menu")
- ("fsm" :external nil)
+ ("fsm" :external nil)
("gcmh" :external "https://gitlab.com/koral/gcmh")
("ggtags" :external "https://github.com/leoliu/ggtags")
("gited" :external nil)
@@ -99,6 +99,7 @@
("gnome-c-style" :external "https://github.com/ueno/gnome-c-style.git")
("gnorb" :external nil) ;; Was "https://github.com/girzel/gnorb"
("gnu-elpa" :external nil)
+ ("gnugo" :external nil)
("gpastel" :external
"https://gitlab.petton.fr/DamienCassou/gpastel")
("greader" :external
"https://gitlab.com/michelangelo-rodriguez/greader")
("guess-language" :external
"https://github.com/tmalsburg/guess-language.el")
@@ -115,7 +116,7 @@
("leaf" :external "https://github.com/conao3/leaf.el")
("let-alist" :core "lisp/emacs-lisp/let-alist.el")
("lmc" :external nil)
- ("load-dir" :external nil)
+ ("load-dir" :external nil)
("map" :core "lisp/emacs-lisp/map.el")
("markchars" :external nil)
("math-symbol-lists" :external
"https://github.com/vspinu/math-symbol-lists.git")
@@ -130,13 +131,14 @@
("names" :external "http://github.com/Malabarba/names")
("nhexl-mode" :external nil)
("nlinum" :external nil)
+ ("ntlm" :core "lisp/net/ntlm.el")
("num3-mode" :external nil)
("objed" :external "https://github.com/clemera/objed")
("omn-mode" :external nil)
+ ("on-screen" :external
"https://github.com/michael-heerdegen/on-screen.el.git")
+ ;;FIXME:("org" :external ??) ;; Need to introduce snapshots!!
("orgalist" :external nil)
("org-edna" :external
"https://savannah.nongnu.org/projects/org-edna-el") ;URL?
- ("ntlm" :core "lisp/net/ntlm.el")
- ("on-screen" :external
"https://github.com/michael-heerdegen/on-screen.el.git")
("pabbrev" :external "https://github.com/phillord/pabbrev.git")
("parsec" :external
"https://github.com/cute-jumper/parsec.el.git")
("peg" :external) ;Was in
"https://github.com/ellerh/peg.el"
@@ -154,7 +156,6 @@
;; -- -- pspp-mode.el
("pspp-mode" :external nil) ;; Was
"https://git.sv.gnu.org/r/pspp.git"
("python" :core "lisp/progmodes/python.el")
- ;;FIXME:("org" :external ??) ;; Need to introduce snapshots!!
("rbit" :external nil)
("rcirc-color" :external nil)
("realgud" :external "https://github.com/realgud/realgud")
diff --git a/packages/gnugo/.dir-locals.el b/packages/gnugo/.dir-locals.el
deleted file mode 100644
index 8941f58..0000000
--- a/packages/gnugo/.dir-locals.el
+++ /dev/null
@@ -1,5 +0,0 @@
-;;; .dir-locals.el
-
-((emacs-lisp-mode . ((indent-tabs-mode . nil))))
-
-;;; .dir-locals.el ends here
diff --git a/packages/gnugo/HACKING b/packages/gnugo/HACKING
deleted file mode 100644
index f2b0f4e..0000000
--- a/packages/gnugo/HACKING
+++ /dev/null
@@ -1,86 +0,0 @@
-HACKING gnugo -*- org -*-
-
-This file is both a guide for newcomers and a todo list for oldstayers.
-
-* next
-*** newbie support
-***** "don't panic" button :-D
-***** on gnugo.el load, check
[[file:gnugo.el::defvar.gnugo-program][gnugo-program]], set "ready" state
-***** rat concessions :-/
-***** (?) ootb ‘gnugo-image-display-mode’ in ‘gnugo-start-game-hook’
-* fix bugs
-*** empty tree from many back/forw
-***** intermittent, grr
-***** manifests as ‘()’ (empty list) in .sgf (on write)
-*** {next,previous}-line weirdness in the presence of images
-*** no error-handling in SGF parsing
-* performance
-*** ‘compare-strings’ approach too clever/slow :-/
-*** cache frolic fruits
-* ideas / wishlist
-*** wrap GTP ‘loadsgf’ completely
-*** revamp image support
-***** DONE zonk ‘require’
-***** define simple API
-*** talk GTP over the network
- (?) pending [[wrap GTP ‘loadsgf’ completely]]
-*** make gnugo (the external program) support query (read-only) thread
-*** extend GNUGO Board mode to manage another subprocess for analysis only
-*** command to label a position
-*** SGF tree traversal
-***** DONE monkey mind
-***** TODO monkey body
-*** "undo undo undoing"
-***** integrate Emacs undo, GTP undo, subgame branching
-***** (?) use [[file:../undo-tree/][../undo-tree/]]
-*** make buffer name format configurable (but enforce uniqueness)
-*** more tilde escapes for
[[file:gnugo.el::defvar.gnugo-mode-line][gnugo-mode-line]]
-*** make veneration configurable (see also [[SVG display]])
-*** animation finery
-***** make more configurable
-***** lift same-color-stones-only restriction
-***** allow sequencing rather than lock-step
-***** include sound
-*** plunder el-go (grok [[info:eieio.info][EIEIO]] first)
-***** SVG display
- pending [[revamp image support]]
-***** (?) SGF support
-******* IR compat
-******* error handling
-***** (?) other "backends"
- pending [[talk GTP over the network]]
-*** [your hacking ideas here!]
-* tested with (newest first)
- | Emacs | GNU Go |
- |-----------+--------|
- | 26.0.50.6 | 3.8 |
- | 24.3.50.3 | 3.8 |
- | ? | 3.6 |
- | ? | 3.4 |
- | ? | 3.3.15 |
- | 22.0.50 | ? |
- | 21.3 | ? |
- |-----------+--------|
- | <l> | <l> |
-* ChangeLog discipline
-*** based on [[info:standards#Change%20Logs][GNU Coding Standards]]
-*** commit-message format
-***** basic: TITLE LF LF [DISCUSSION...] LF LF CLASSIC
-***** short: TITLE-FRAGMENT "; nfc." [LF LF DISCUSSION...]
-*** don't bother w/ ChangeLog for "short" commit-message format
-* copyright policy
-*** update every year, unconditionally
-*** (if (< 2 (- END BEGIN)) RANGE INDIVIDUAL)
-* other conventions: see [[file:.dir-locals.el][.dir-locals.el]]
-* NEWS nostalgia
-(with-current-buffer (find-file "NEWS")
- (highlight-phrase "[0-9][.][0-9][.][0-9]+\\|[0-9]+[.][.][0-9]+"
- 'hi-red-b))
-* etc
-#+odd
-
-
-Copyright (C) 2014-2020 Free Software Foundation, Inc.
-
-Copying and distribution of this file, with or without modification,
-are permitted provided the copyright notice and this notice are preserved.
diff --git a/packages/gnugo/NEWS b/packages/gnugo/NEWS
deleted file mode 100644
index c35de50..0000000
--- a/packages/gnugo/NEWS
+++ /dev/null
@@ -1,240 +0,0 @@
-NEWS for gnugo.el (et al)
-See the end for copying conditions.
-
-NB: "RCS: X..Y " means that the particular release includes
- changes in the RCS repo, revision 1.X through 1.Y (inclusive).
-
-
-- 3.1.1 | 2020-10-24
- - bugfix: use ‘cursor-intangible-mode’ properly
- - new func: gnugo-imgen-fit-window-height/no-grid-bottom
- - more docstrings
-
-- 3.1.0 | 2017-02-17
- - THANKS file includes pre-ELPA people
- - bugfix: refresh no longer clobbers dead-group indication
- - echo area messages (normal and error) more conventional
- - ‘C’ (gnugo-comment) defaults to root node if no played stone at point
- - ‘C-c C-p’ (gnugo-describe-internal-properties) output more spacious
- - ‘C-u F’ (gnugo-display-final-score) stores additional SGF properties
- - ‘TB’ -- Black Territory
- - ‘TW’ -- White Territory
- - ‘MA’ -- Mark (to indicate seki stones)
- - ‘DD’ -- Dim Points (to indicate dead stones)
- - game-over seki groups (if any) indicated on board
- - changes to programming interface
- - dropped data structures (unused => NOT backward-incompatible)
- - ‘gnugo-board-mode-syntax-table’
- - ‘gnugo-board-mode-abbrev-table’
- - game-over "group" formalized: ‘((CPROP [OVERLAY[...]]) POS[...])’
- - game-over data includes seki groups
- - ‘gnugo-goto-pos’ returns buffer position
- - new abstraction: ‘gnugo-aqr’
-
-- 3.0.2 | 2017-02-05
- - portability fix (Emacs 25.1): use Cursor Intangible mode, if available
- - verse meter fix (for those who read source code)
- - new THANKS file
-
-- 3.0.1 | 2017-01-15
- - cleaner quoting in docstrings
- - portabilty fixes (Emacs 25.1)
-
-- 3.0.0 | 2014-07-22
- - bugfixes
- - on write, use ‘\’ to escape certain chars
- - preserve whitespace for value type ‘text’
- - don't special-case property value type ‘none’
- - handle subtrees on write
- - display "resign" as "resign" in move history (amazing!)
- - avoid clobbering SGF property ‘EV’ on resignation
- - follow main line in subtrees on read
- - for ‘F’ forced PASS moves, keep subproc informed as well
- - proper support for ‘-l FILENAME’ / ‘--infile FILENAME’
- - dropped support for ‘gnugo-program’ of form "PROGRAM OPTIONS..."
- - dropped command: ‘t’ (gnugo-toggle-dead-group)
- - changes to ‘gnugo-xpms’
- - now a normal var, and not a feature
- - value can be a function to compute XPMs
- - ‘gnugo-image-display-mode’ replaces ‘gnugo-toggle-image-display-command’
- - ‘gnugo-grid-mode’ replaces ‘gnugo-toggle-grid’
- - PASS for SZ <= 19 normalized to "" on read, written as ""
- - ‘=’ also displays move number of the stone (if any) at that position
- - ‘C-u F’ adds the (abbreviated) blurb as a comment to the last node
- - new keybinding for ‘gnugo-undo-one-move’: M-u
- - you can play a move for GNU Go, e.g., after ‘M-u’
- - ‘gnugo-undo-one-move’ can optionally arrange for you to play next
- - new command: ‘S’ (gnugo-request-suggestion)
- - new command: ‘C’ (gnugo-comment)
- - new command: ‘o’ (gnugo-oops)
- - new command: ‘O’ (gnugo-okay)
- - new command: ‘L’ (gnugo-frolic-in-the-leaves)
- - new command: ‘C-c C-a’ (gnugo-assist-mode)
- - new command: ‘C-c C-z’ (gnugo-zombie-mode)
- - new var: gnugo-undo-reaction
- - new major mode: GNUGO Frolic (gnugo-frolic-mode)
- - separate feature/file: ‘gnugo-frolic’
- - ‘gnugo-frolic-in-the-leaves’ autoloaded
- - new support for dynamic XPM generation
- - separate feature/file: ‘gnugo-imgen’
- - func ‘gnugo-imgen-create-xpms’ suitable for ‘gnugo-xpms’ (see above)
- - GNUGO Board mode now derived from Special mode
- - position arg validated for direct GTP commands ‘undo’, ‘gg-undo’
- - undo commands no longer signal error on overkill
- - SGF prop ‘AP’ set only for modified gametrees
- - SGF I/O commands change ‘default-directory’
- - performance improvements
- - of interest to hackers (see source, BI => backward incompatible)
- - dropped var: ‘gnugo-inhibit-refresh’ (BI)
- - ‘gnugo/sgf-read-file’ renamed to ‘gnugo/sgf-create’ and enhanced
- - ‘:sgf-gametree’ internal representation inverted (BI)
- - ‘gnugo-magic-undo’ internalized
- - new func: ‘gnugo-current-player’
- - new hook: ‘gnugo-start-game-hook’
- - ‘gnugo-board-mode-hook’ now unsuitable for prop munging (BI)
- - changes to ‘gnugo-move-history’
- - dropped ‘(gnugo-move-history 'count)’ (BI)
- - returns last two moves w/ RSEL ‘two’
- - returns position of last placed stone w/ RSEL ‘bpos’ + 2nd arg COLOR
-
-- 2.3.1 | 2014-02-27
- - portability fixes
-
-- 2.3.0 | 2014-02-24
- - now part of ELPA, tweaked for GNU Emacs 24.x
- - dropped support for XEmacs and older Emacs
- - use ‘user-error’ for user errors
- - bugfixes
- - handle sudden jump in captured stones correctly
- - rename hook-communication var w/ "gnugo-" prefix
- - (w/ images) grid top/bottom row spacing
- - on SGF save/load indicate buffer not modified
- - documentation improvements
- - version numbering scheme documented: MAJOR.MINOR.PATCH
- - keybinding constructs for ‘gnugo’ and ‘gnugo-board-mode’
- - message for worm/dragon animation avoids underscore
- - new commands
- - ‘A’ (gnugo-switch-to-another)
- - ‘_’ and ‘M-_’ (gnugo-boss-is-near) -- was ‘bury-buffer’
- - new keybinding for ‘gnugo-undo-two-moves’: DEL
-
-- 2.2.14 | 2008-03-03
- - start error message w/ a capital letter
- - use ‘(error "%s" X)’ instead of ‘(error X)’
- - improve ‘gnugo-animation-string’ docstring
-
-- 2.2.13 | 2006-04-10
- - grid (letters and numbers) visibility can be toggled
- - display bug workaround
- - performance improvements
-
-- 2.2.12 | 2006-04-06
- - bugfix: handle "" as "PASS"
-
-- 2.2.11 | 2005-04-06
- - new mode-line specifier: ‘~m’
- - directory no longer accepted as SGF "file name"
-
-- 2.2.10 | 2005-02-04
- - bugfix: detect "game over" more precisely
- - new command: ‘C-c C-p’ (gnugo-describe-internal-properties)
- - don't include comment in sgf write
- - set ‘AP’ (application) property in gametree
-
-- 2.2.9 | 2004-12-29
- - backward-portability fix
-
-- 2.2.8 | 2004-11-15
- - new command: ‘h’ (gnugo-move-history)
- - improve font-lock support
- - support "count of moves" SPEC via prefix-arg to ‘U’
-
-- 2.2.7 | 2004-11-10
- - bugfix: inform backend of PASS
- - new command: ‘u’ (gnugo-undo-two-moves)
-
-- 2.2.6 | 2004-11-05
- - new commands
- - ‘l’ (gnugo-read-sgf-file)
- - ‘U’ (lambda that calls ‘gnugo-magic-undo’)
- - doc improvements
- - make some load-time actions one-shot
-
-- 2.2.5 | 2004-11-02
- - bugfix: make load-time actions referentially-transparent
- - require Emacs w/ ‘gethash’, ‘puthash’, ‘make-hash-table’
-
-- 2.2.4 | 2004-11-01
- - backward-portability fixes
-
-- 2.2.3 | 2004-10-30
- - backward-portability fixes
- - new command: ‘R’ (gnugo-resign)
-
-- 2.2.2 | 2004-10-29
- - backward-portability fixes
-
-- 2.2.1 | 2004-09-07
- - SGF bugfix: parse float correctly (for komi)
- - new command: ‘F’ (gnugo-display-final-score)
- - font-locking for "X", "O", "[xo]"
-
-- 2.2.0 | 2004-08-30
- - uncluttered, letters and numbers hidden, board centered
- - buffer name shows last move and current player
- - mode-line customization (var ‘gnugo-mode-line’)
- - new commands
- - ‘=’ -- display current position in echo area
- - ‘s’ (gnugo-write-sgf-file)
- - program option customization (var ‘gnugo-program’)
- - new hooks
- - ‘gnugo-post-move-hook’
- - ‘gnugo-board-mode-hook’
- - multiple independent buffers/games
- - XPM set can be changed on the fly (global and/or local)
- - RCS: 25..26 (1.24-1.26 diff posted 2003-01-28)
-
-- 2.1.0 | 2003-01-10
- - doc fixes
- - add XPM image support
- - new keybinding: ‘;’ (gnugo-command)
- - new commands:
- - ‘i’ -- toggle image display
- - ‘mouse-down-1’ (gnugo-mouse-move)
- - ‘mouse-down-3’ (gnugo-mouse-pass)
- - ‘gnugo-command’ rewrite
- - RCS: 19..24
-
-- 2.0.1 | 2002-11-16
- - more docstrings
- - say "GNU Go" instead of "GNU GO"
- - reverse output order of captured stones
- - new commands: ‘_’ and ‘M-_’ (bury-buffer)
- - simplified buffer management
- - RCS: 15..18
-
-- 2.0.0 | 2002-11-15
- - rewrite to use Go Text Protocol
- - bugfix: don't set process coding system
- - handle change in process status
- - new user var: ‘gnugo-option-history’
- - use calculated screen columns
- - improve error handling in ‘gnugo-cleanup’
- - new command: ‘:’ (gnugo-command)
- - resume game if in progress, w/ confirmation
- - new command: ‘M-_’ (gnugo-bury)
- - autoload gnugo.el on command ‘gnugo’
- - doc improvements
- - RCS: 1..14
-
-
- Local Variables:
- mode: outline
- outline-regexp: "\\([ ][ ]\\)*- "
- End:
-
-_____________________________________________________________________
-Copyright (C) 2014-2020 Free Software Foundation, Inc.
-
-Copying and distribution of this file, with or without modification,
-are permitted provided the copyright notice and this notice are preserved.
diff --git a/packages/gnugo/README b/packages/gnugo/README
deleted file mode 100644
index 897feab..0000000
--- a/packages/gnugo/README
+++ /dev/null
@@ -1,6 +0,0 @@
-This directory contains gnugo.el and other files.
-These work w/ GNU Go:
-
- http://www.gnu.org/software/gnugo
-
-and any other program that speaks the Go Text Protocol.
diff --git a/packages/gnugo/THANKS b/packages/gnugo/THANKS
deleted file mode 100644
index dd994dd..0000000
--- a/packages/gnugo/THANKS
+++ /dev/null
@@ -1,13 +0,0 @@
-These people have helped to improve gnugo.el (et al).
-
- Stefan Monnier
- Paul Eggert
- Juanma Barranquero
- Mike FABIAN
- Len Trigg
- Gunnar Farnebäck
- Douglas Ridgway
- Daniel Bump
-
-If you are not here, but should be, that's a bug -- please accept
-our apologies for the oversight, and report it, so we can DTRT!
diff --git a/packages/gnugo/gnugo-frolic.el b/packages/gnugo/gnugo-frolic.el
deleted file mode 100644
index 4129971..0000000
--- a/packages/gnugo/gnugo-frolic.el
+++ /dev/null
@@ -1,509 +0,0 @@
-;;; gnugo-frolic.el --- gametree in a buffer -*- lexical-binding: t -*-
-
-;; Copyright (C) 2014-2020 Free Software Foundation, Inc.
-
-;; Author: Thien-Thi Nguyen <ttn@gnu.org>
-;; Maintainer: Thien-Thi Nguyen <ttn@gnu.org>
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Code:
-
-(require 'cl-lib)
-(require 'gnugo)
-(require 'ascii-art-to-unicode) ; for `aa2u'
-
-(defvar gnugo-frolic-mode-map
- (let ((map (make-sparse-keymap)))
- (suppress-keymap map)
- (mapc (lambda (pair)
- (define-key map (car pair) (cdr pair)))
- '(("q" . gnugo-frolic-quit)
- ("Q" . gnugo-frolic-quit)
- ("\C-q" . gnugo-frolic-quit)
- ("C" . gnugo-frolic-quit) ; like ‘View-kill-and-leave’
- ("\C-b" . gnugo-frolic-backward-branch)
- ("\C-f" . gnugo-frolic-forward-branch)
- ("\C-p" . gnugo-frolic-previous-move)
- ("\C-n" . gnugo-frolic-next-move)
- ("t" . gnugo-frolic-tip-move)
- ("j" . gnugo-frolic-exchange-left)
- ("J" . gnugo-frolic-rotate-left)
- ("k" . gnugo-frolic-exchange-right)
- ("K" . gnugo-frolic-rotate-right)
- ("\C-m" . gnugo-frolic-set-as-main-line)
- ("\C-\M-p" . gnugo-frolic-prune-branch)
- ("o" . gnugo-frolic-return-to-origin)))
- map)
- "Keymap for GNUGO Frolic mode.")
-
-(defvar gnugo-frolic-parent-buffer nil)
-(defvar gnugo-frolic-origin nil)
-
-(define-derived-mode gnugo-frolic-mode special-mode "GNUGO Frolic"
- "A special mode for manipulating a GNUGO gametree."
- (setq truncate-lines t)
- (buffer-disable-undo))
-
-(defun gnugo-frolic-quit ()
- "Kill GNUGO Frolic buffer and switch to its parent buffer."
- (interactive)
- (let ((bye (current-buffer)))
- (switch-to-buffer (when (buffer-live-p gnugo-frolic-parent-buffer)
- gnugo-frolic-parent-buffer))
- (kill-buffer bye)))
-
-(defun gnugo-frolic-return-to-origin ()
- "Move point to the board's current position."
- (interactive)
- (if (not gnugo-frolic-origin)
- (message "No origin")
- (goto-char gnugo-frolic-origin)
- (recenter (- (count-lines (line-beginning-position)
- (point-max))))))
-
-;;;###autoload
-(defun gnugo-frolic-in-the-leaves ()
- "Display the game tree in a *GNUGO Frolic* buffer.
-This looks something like:
-
- 1 B -- E7 E7 E7 E7
- 2 W -- K10 K10 K10 K10
- 3 B -- E2 E2 E2 E2
- 4 W -- J3 J3 J3 J3
- 5 B -- A6 A6 A6 A6
- 6 W -- C9 C9 C9 C9
- │
- ├─────┬─────┐
- │ │ │
- 7 B -- H7 !B8 C8 C8
- │
- ├─────┐
- │ │
- 8 W -- D9 D9 D9 E9
- 9 B -- H8 H8
- 10 W -- PASS PASS
- 11 B -- H5 PASS
- 12 W -- PASS
- 13 B -- *PASS
-
-with 0, 1, ... N (in this case N is 3) in the header line
-to indicate the branches. Branch 0 is the \"main line\".
-Point (* in this example) indicates the current position,
-\"!\" indicates comment properties (e.g., B8, branch 1),
-and moves not actually on the game tree (e.g., E7, branch 3)
-are dimmed. Type \\[describe-mode] in that buffer for details."
- (interactive)
- (let* ((buf (get-buffer-create (concat (gnugo-get :diamond)
- "*GNUGO Frolic*")))
- (from (or gnugo-frolic-parent-buffer
- (current-buffer)))
- ;; todo: use defface once we finally succumb to ‘customize’
- (dimmed-node-face (list :inherit 'default
- :foreground "gray50"))
- (tree (gnugo-get :sgf-gametree))
- (ends (copy-sequence (gnugo--tree-ends tree)))
- (mnum (gnugo--tree-mnum tree))
- (seen (gnugo--mkht))
- (soil (gnugo--mkht))
- (width (length ends))
- (lanes (number-sequence 0 (1- width)))
- (monkey (gnugo-get :monkey))
- (as-pos (gnugo--as-pos-func))
- (at (car (aref monkey 0)))
- (bidx (aref monkey 1))
- (valid (cl-map 'vector (lambda (end)
- (gethash (car end) mnum))
- ends))
- (max-move-num (apply 'max (append valid nil)))
- (inhibit-read-only t)
- finish)
- (cl-flet
- ((on (node)
- (gethash node seen))
- (emph (s face)
- (propertize s 'face face))
- (fsi (properties fmt &rest args)
- (insert (apply 'propertize
- (apply 'format fmt args)
- properties))))
- ;; breathe in
- (cl-loop
- for bx below width
- do (cl-loop
- with fork
- for node in (aref ends bx)
- do (if (setq fork (on node))
- (cl-flet
- ((tip-p (bix)
- ;; todo: ignore non-"move" nodes
- (eq node (car (aref ends bix))))
- (link (other)
- (cl-pushnew other (gethash node soil))))
- (unless (tip-p bx)
- (unless (tip-p fork)
- (link fork))
- (link bx)))
- (puthash node bx seen))
- until fork))
- ;; breathe out
- (switch-to-buffer buf)
- (gnugo-frolic-mode)
- (erase-buffer)
- (setq header-line-format
- (let ((full (concat
- (make-string 11 ?\s)
- (mapconcat (lambda (n)
- (format "%-5s" n))
- lanes
- " "))))
- `((:eval
- (funcall
- ,(lambda ()
- (cl-flet
- ((sp (w) (propertize
- " " 'display
- `(space :width ,w))))
- (concat
- (when (eq 'left scroll-bar-mode)
- (let ((w (or scroll-bar-width
- (frame-parameter
- nil 'scroll-bar-width)))
- (cw (frame-char-width)))
- (sp (if w
- (/ w cw)
- 2))))
- (let ((fc (fringe-columns 'left t)))
- (unless (zerop fc)
- (sp fc)))
- (condition-case nil
- (substring full (window-hscroll))
- (error ""))))))))))
- (set (make-local-variable 'gnugo-frolic-parent-buffer) from)
- (set (make-local-variable 'gnugo-state)
- (buffer-local-value 'gnugo-state from))
- (cl-loop
- with props
- for n ; move number
- from max-move-num downto 1
- do (setq props (list 'n n))
- do
- (cl-loop
- with (move forks br)
- initially (progn
- (goto-char (point-min))
- (fsi props
- "%3d %s -- "
- n (aref ["W" "B"] (logand 1 n))))
- for bx below width
- do (let* ((node (unless (< (aref valid bx) n)
- ;; todo: ignore non-"move" nodes
- (pop (aref ends bx))))
- (zow `(bx ,bx ,@props))
- (ok (when node
- (= bx (on node))))
- (comment (when ok
- (gnugo-aqr :C node)))
- (s (cond ((not node) "")
- ((not (setq move (gnugo--move-prop node))) "-")
- (t (funcall as-pos (cdr move))))))
- (when comment
- (push comment zow)
- (push 'help-echo zow))
- (when (and ok (setq br (gethash node soil)))
- (push (cons bx (sort br '<))
- forks))
- (fsi zow
- "%c%-5s"
- (if comment ?! ?\s)
- (cond ((and (eq at node)
- (or ok (= bx bidx)))
- (when (= bx bidx)
- (setq finish (point-marker)))
- (emph s (list :inherit 'default
- :foreground (frame-parameter
- nil 'cursor-color))))
- ((not ok)
- (emph s dimmed-node-face))
- (t s))))
- finally do
- (when (progn (fsi props "\n")
- (setq forks (nreverse forks)))
- (let* ((margin (make-string 11 ?\s))
- (heads (mapcar #'car forks))
- (tails (mapcar #'cdr forks)))
- (cl-flet*
- ((spaced (lanes func)
- (mapconcat func lanes " "))
- ;; live to play ~ ~ ()
- ;; play to learn (+) (-) . o O
- ;; learn to live --ttn .M. _____U
- (dashed (lanes func) ;;; _____ ^^^^
- (mapconcat func lanes "-----"))
- (cnxn (lanes set)
- (spaced lanes (lambda (bx)
- (if (memq bx set)
- "|"
- " "))))
- (pad-unless (condition)
- (if condition
- ""
- " "))
- (edge (set)
- (insert margin
- (cnxn lanes set)
- "\n")))
- (edge heads)
- (cl-loop
- with bef
- for ls on forks
- do (let* ((one (car ls))
- (yes (append
- ;; "aft" heads
- (mapcar 'car (cdr ls))
- ;; ‘bef’ tails
- (apply 'append (mapcar 'cdr bef))))
- (ord (sort one '<))
- (beg (car ord))
- (end (car (last ord))))
- (cl-flet
- ((also (b e) (cnxn (number-sequence b e)
- yes)))
- (insert
- margin
- (also 0 (1- beg))
- (pad-unless (zerop beg))
- (dashed (number-sequence beg end)
- (lambda (bx)
- (cond ((memq bx ord) "+")
- ((memq bx yes) "|")
- (t "-"))))
- (pad-unless (>= end width))
- (also (1+ end) (1- width))
- "\n"))
- (push one bef)))
- (edge (apply 'append tails))
- (aa2u (line-beginning-position
- (- (1+ (length forks))))
- (point))))))))
- (when finish
- (set (make-local-variable 'gnugo-frolic-origin) finish)
- (gnugo-frolic-return-to-origin))))
-
-(defun gnugo--awake (how)
- ;; Valid HOW elements:
- ;; require-valid-branch
- ;; (line . numeric)
- ;; (line . move-string)
- ;; (omit . [VAR...])
- ;; Invalid elements blissfully ignored. :-D
- (let* ((tree (gnugo-get :sgf-gametree))
- (ends (gnugo--tree-ends tree))
- (width (length ends))
- (monkey (gnugo-get :monkey))
- (line (cl-case (gnugo-aqr 'line how)
- (numeric
- (count-lines (point-min) (line-beginning-position)))
- (move-string
- (save-excursion
- (when (re-search-backward "^ *[0-9]+ [BW]" nil t)
- (match-string 0))))
- (t nil)))
- (col (current-column))
- (a (unless (> 10 col)
- (let ((try (/ (- col 10)
- 6)))
- (unless (<= width try)
- try))))
- (rv (list a)))
- (when (memq 'require-valid-branch how)
- (unless a
- (user-error "No branch here")))
- (cl-loop
- with omit = (gnugo-aqr 'omit how)
- for (name . value) in `((line . ,line)
- (bidx . ,(aref monkey 1))
- (monkey . ,monkey)
- (width . ,width)
- (ends . ,ends)
- (tree . ,tree))
- do (unless (memq name omit)
- (push value rv)))
- rv))
-
-(defmacro gnugo--awakened (how &rest body)
- (declare (indent 1))
- `(cl-destructuring-bind
- ,(cl-loop
- with omit = (gnugo-aqr 'omit how)
- with ls = (list 'a)
- for name in '(line bidx monkey
- width ends
- tree)
- do (unless (memq name omit)
- (push name ls))
- finally return ls)
- (gnugo--awake ',how)
- ,@body))
-
-(defsubst gnugo--move-to-bcol (bidx)
- (move-to-column (+ 10 (* 6 bidx))))
-
-(defun gnugo--swiz (direction &optional blunt)
- (gnugo--awakened (require-valid-branch
- (omit tree)
- (line . numeric))
- (let* ((b (cond ((numberp blunt)
- (unless (and (< -1 blunt)
- (< blunt width))
- (user-error "No such branch: %s" blunt))
- blunt)
- (t (mod (+ direction a) width))))
- (flit (if blunt (lambda (n)
- (cond ((= n a) b)
- ((= n b) a)
- (t n)))
- (lambda (n)
- (mod (+ direction n) width))))
- (was (copy-sequence ends))
- (new-bidx (funcall flit bidx)))
- (cl-loop
- for bx below width
- do (aset ends (funcall flit bx)
- (aref was bx)))
- (unless (= new-bidx bidx)
- (aset monkey 1 new-bidx))
- (gnugo-frolic-in-the-leaves)
- (goto-char (point-min))
- (forward-line line)
- (gnugo--move-to-bcol b))))
-
-(defun gnugo-frolic-exchange-left ()
- "Exchange the current branch with the one to its left."
- (interactive)
- (gnugo--swiz -1 t))
-
-(defun gnugo-frolic-rotate-left ()
- "Rotate all branches left."
- (interactive)
- (gnugo--swiz -1))
-
-(defun gnugo-frolic-exchange-right ()
- "Exchange the current branch with the one to its right."
- (interactive)
- (gnugo--swiz 1 t))
-
-(defun gnugo-frolic-rotate-right ()
- "Rotate all branches right."
- (interactive)
- (gnugo--swiz 1))
-
-(defun gnugo-frolic-set-as-main-line ()
- "Make the current branch the main line."
- (interactive)
- (gnugo--swiz nil 0))
-
-(defun gnugo-frolic-prune-branch ()
- "Remove the current branch from the gametree.
-This fails if there is only one branch in the tree.
-This fails if the monkey is on the current branch
-\(a restriction that will probably be lifted Real Soon Now\)."
- (interactive)
- (gnugo--awakened (require-valid-branch
- (line . move-string))
- ;; todo: define meaningful eviction semantics; remove restriction
- (when (= a bidx)
- (user-error "Cannot prune with monkey on branch"))
- (when (= 1 width)
- (user-error "Cannot prune last remaining branch"))
- (let ((new (append ends nil)))
- ;; Explicit ignorance avoids byte-compiler warning.
- (ignore (pop (nthcdr a new)))
- (gnugo--set-tree-ends tree new))
- (when (< a bidx)
- (aset monkey 1 (cl-decf bidx)))
- (gnugo-frolic-in-the-leaves)
- (when line
- (goto-char (point-min))
- (search-forward line)
- (gnugo--move-to-bcol (min a (- width 2))))))
-
-(defun gnugo--sideways (backwards n)
- (gnugo--awakened ((omit tree ends monkey bidx line))
- (gnugo--move-to-bcol (mod (if backwards
- (- (or a width) n)
- (+ (or a -1) n))
- width))))
-
-(defun gnugo-frolic-backward-branch (&optional n)
- "Move backward N (default 1) branches."
- (interactive "p")
- (gnugo--sideways t n))
-
-(defun gnugo-frolic-forward-branch (&optional n)
- "Move forward N (default 1) branches."
- (interactive "p")
- (gnugo--sideways nil n))
-
-(defun gnugo--vertical (n direction)
- (when (> 0 n)
- (setq n (- n)
- direction (- direction)))
- (gnugo--awakened ((line . numeric)
- (omit tree ends width monkey bidx))
- (let ((stop (if (> 0 direction)
- 0
- (max 0 (1- (count-lines (point-min)
- (point-max))))))
- (col (unless a
- (current-column))))
- (cl-loop
- while (not (= line stop))
- do (cl-loop
- do (progn
- (forward-line direction)
- (cl-incf line direction))
- until (get-text-property (point) 'n))
- until (zerop (cl-decf n)))
- (if a
- (gnugo--move-to-bcol a)
- (move-to-column col)))))
-
-(defun gnugo-frolic-previous-move (&optional n)
- "Move to the Nth (default 1) previous move."
- (interactive "p")
- (gnugo--vertical n -1))
-
-(defun gnugo-frolic-next-move (&optional n)
- "Move to the Nth (default 1) next move."
- (interactive "p")
- (gnugo--vertical n 1))
-
-(defun gnugo-frolic-tip-move ()
- "Move to the tip of the current branch."
- (interactive)
- (gnugo--awakened ((omit line bidx monkey width)
- require-valid-branch)
- (goto-char (point-max))
- (let ((mnum (gnugo--tree-mnum tree))
- (node (car (aref ends a))))
- (re-search-backward (format "^%3d" (gethash node mnum)))
- (gnugo--move-to-bcol a))))
-
-;;;---------------------------------------------------------------------------
-;;; that's it
-
-(provide 'gnugo-frolic)
-
-;;; gnugo-frolic.el ends here
diff --git a/packages/gnugo/gnugo-imgen.el b/packages/gnugo/gnugo-imgen.el
deleted file mode 100644
index 4044256..0000000
--- a/packages/gnugo/gnugo-imgen.el
+++ /dev/null
@@ -1,258 +0,0 @@
-;;; gnugo-imgen.el --- image generation -*- lexical-binding: t -*-
-
-;; Copyright (C) 2014-2020 Free Software Foundation, Inc.
-
-;; Author: Thien-Thi Nguyen <ttn@gnu.org>
-;; Maintainer: Thien-Thi Nguyen <ttn@gnu.org>
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This file provides func `gnugo-imgen-create-xpms', suitable as
-;; value for `gnugo-xpms', and several variables to configure it:
-;;
-;; `gnugo-imgen-styles'
-;; `gnugo-imgen-style'
-;; `gnugo-imgen-sizing-function'
-;;
-;; There is also one command: `gnugo-imgen-clear-cache'.
-
-;;; Code:
-
-(require 'xpm)
-(require 'xpm-m2z)
-(require 'cl-lib)
-
-(defvar gnugo-imgen-styles
- '((d-bump ; thanks
- :background "#FFFFC7C75252"
- :grid-lines "#000000000000"
- :circ-edges "#C6C6C3C3C6C6"
- :white-fill "#FFFFFFFFFFFF"
- :black-fill "#000000000000")
- (ttn ; this guy must live in a cave
- :background "#000000000000"
- :grid-lines "#AAAA88885555"
- :circ-edges "#888888888888"
- :white-fill "#CCCCCCCCCCCC"
- :black-fill "#444444444444"))
- "Alist of styles suitable for `gnugo-imgen-create-xpms'.
-The key is a symbol naming the style. The value is a plist.
-Here is a list of recognized keywords and their meanings:
-
- :background -- string that names a color in XPM format, such as
- :grid-lines \"#000000000000\" or \"black\"; the special string
- :circ-edges \"None\" makes that component transparent
- :white-fill
- :black-fill
-
-All keywords are required and color values cannot be nil.
-This restriction may be lifted in the future.")
-
-(defvar gnugo-imgen-style nil
- "Which style in `gnugo-imgen-styles' to use.
-If nil, `gnugo-imgen-create-xpms' defaults to the first one.")
-
-(defvar gnugo-imgen-sizing-function 'gnugo-imgen-fit-window-height
- "Function to compute XPM image size from board size.
-This is called with one arg, integer BOARD-SIZE, and should return
-a number (float or integer), the number of pixels for the side of
-a square position on the board. A value less than 8 is taken as 8.")
-
-(defvar gnugo-imgen-cache (make-hash-table :test 'equal))
-
-(defun gnugo-imgen-clear-cache ()
- "Clear the cache."
- (interactive)
- (clrhash gnugo-imgen-cache))
-
-(defun gnugo-imgen--fit (board-size ignored-grid-lines)
- (cl-destructuring-bind (L top R bot)
- (window-inside-absolute-pixel-edges)
- (ignore L R)
- (/ (float (- bot top (* (frame-char-height)
- ignored-grid-lines)))
- board-size)))
-
-(defun gnugo-imgen-fit-window-height (board-size)
- "Return the dimension (in pixels) of a square for BOARD-SIZE.
-This uses the TOP and BOTTOM components as returned by
-`window-inside-absolute-pixel-edges' and subtracts twice
-the `frame-char-height' (to leave space for the grid)."
- (gnugo-imgen--fit board-size 2))
-
-(defun gnugo-imgen-fit-window-height/no-grid-bottom (board-size)
- "Return the dimension (in pixels) of a square for BOARD-SIZE.
-This uses the TOP and BOTTOM components as returned by
-`window-inside-absolute-pixel-edges' and subtracts the
-`frame-char-height' (to leave top-line space for the grid)."
- (gnugo-imgen--fit board-size 1))
-
-(defconst gnugo-imgen-palette '((32 . :background)
- (?. . :grid-lines)
- (?X . :circ-edges)
- (?- . :black-fill)
- (?+ . :white-fill)))
-
-(defun gnugo-imgen-create-xpms-1 (square style)
- (let* ((kws (mapcar 'cdr gnugo-imgen-palette))
- (roles (mapcar 'symbol-name kws))
- (palette (cl-loop
- for px in (mapcar 'car gnugo-imgen-palette)
- for role in roles
- collect (cons px (format "s %s" role))))
- (resolved (cl-loop
- with parms = (copy-sequence style)
- for role in roles
- for kw in kws
- collect (cons role (plist-get parms kw))))
- (sq-m1 (1- square))
- (half (/ sq-m1 2.0))
- (half-m1 (truncate (- half 0.5)))
- (half-p1 (truncate (+ half 0.5)))
- (background (make-vector 10 nil))
- (foreground (make-vector 4 nil))
- rv)
- (cl-flet
- ((workbuf (n)
- (xpm-generate-buffer (format "%d_%d" n square)
- square square 1 palette))
- (replace-from (buffer)
- (erase-buffer)
- (insert-buffer-substring buffer)
- (xpm-grok t))
- (nine-from-four (N E W S)
- (list (list E S)
- (list E W S)
- (list W S)
- (list N E S)
- (list N E W S)
- (list N W S)
- (list N E )
- (list N E W )
- (list N W )))
- (mput-points (px ls)
- (dolist (coord ls)
- (apply 'xpm-put-points px coord))))
- ;; background
- (cl-loop
- for place from 1 to 9
- for parts
- in (cl-flet*
- ((vline (x y1 y2) (list (list x (cons y1 y2))))
- (v-expand (y1 y2) (append (vline half-m1 y1 y2)
- (vline half-p1 y1 y2)))
- (hline (y x1 x2) (list (list (cons x1 x2) y)))
- (h-expand (x1 x2) (append (hline half-m1 x1 x2)
- (hline half-p1 x1 x2))))
- (nine-from-four (v-expand 0 half-p1)
- (h-expand half-m1 sq-m1)
- (h-expand 0 half-p1)
- (v-expand half-m1 sq-m1)))
- do (aset background place
- (with-current-buffer (workbuf place)
- (dolist (part parts)
- (mput-points ?. part))
- (current-buffer))))
- ;; foreground
- (cl-flet
- ((circ (radius)
- (xpm-m2z-circle half half radius)))
- (cl-loop
- with stone = (circ (truncate half))
- with minim = (circ (/ square 9))
- for n below 4
- do (aset foreground n
- (with-current-buffer (workbuf n)
- (cl-flet
- ((rast (form b w)
- (xpm-raster form ?X
- (if (> 2 n)
- b
- w))))
- (if (cl-evenp n)
- (rast stone ?- ?+)
- (replace-from (aref foreground (1- n)))
- (rast minim ?+ ?-))
- (current-buffer))))))
- ;; do it
- (cl-flet
- ((ok (place type finish)
- (goto-char 25)
- (delete-char (- (skip-chars-forward "^1-9")))
- (delete-char 1)
- (insert (format "%s%d" type place))
- (push (cons (cons type place)
- (funcall finish
- :ascent 'center
- :color-symbols resolved))
- rv)))
- (with-current-buffer (workbuf 5)
- (replace-from (aref background 5))
- (xpm-raster
- ;; yes, using an ellipse is bizarre; no, we don't mind;
- ;; maybe, ‘artist-ellipse-generate-quadrant’ is stable.
- (xpm-m2z-ellipse half half 4 4.5)
- ?. t)
- (ok 5 'hoshi 'xpm-finish))
- (cl-loop
- for place from 1 to 9
- for decor in (let ((friends (cons half-m1 half-p1)))
- (nine-from-four (list friends 0)
- (list sq-m1 friends)
- (list 0 friends)
- (list friends sq-m1)))
- do (with-current-buffer (aref background place)
- (ok place 'empty 'xpm-finish))
- do (cl-flet
- ((decorate (px)
- (mput-points px decor)))
- (cl-loop
- for n below 4
- for type in '(bmoku bpmoku wmoku wpmoku)
- do (with-current-buffer (aref foreground n)
- (decorate ?.)
- (ok place type 'xpm-as-xpm)
- (decorate 32)))))
- (mapc 'kill-buffer foreground)
- (nreverse rv)))))
-
-;;;###autoload
-(defun gnugo-imgen-create-xpms (board-size)
- "Return a list of XPM images suitable for BOARD-SIZE.
-The size and style of the images are determined by
-`gnugo-imgen-sizing-function' (rounded down to an even number)
-and `gnugo-imgen-style', respectively. See `gnugo-xpms'.
-
-The returned list is cached; see also `gnugo-imgen-clear-cache'."
- (let* ((square (let ((n (funcall gnugo-imgen-sizing-function
- board-size)))
- (unless (numberp n)
- (error "Invalid BOARD-SIZE: %s" board-size))
- (max 8 (logand (lognot 1) (truncate n)))))
- (style (or (unless gnugo-imgen-style (cdar gnugo-imgen-styles))
- (cdr (assq gnugo-imgen-style gnugo-imgen-styles))
- (error "No style selected")))
- (key (cons square style)))
- (or (gethash key gnugo-imgen-cache)
- (puthash key (gnugo-imgen-create-xpms-1 square style)
- gnugo-imgen-cache))))
-
-;;;---------------------------------------------------------------------------
-;;; that's it
-
-(provide 'gnugo-imgen)
-
-;;; gnugo-imgen.el ends here
diff --git a/packages/gnugo/gnugo.el b/packages/gnugo/gnugo.el
deleted file mode 100644
index 5dbc06a..0000000
--- a/packages/gnugo/gnugo.el
+++ /dev/null
@@ -1,2866 +0,0 @@
-;;; gnugo.el --- play GNU Go in a buffer -*- lexical-binding: t -*-
-
-;; Copyright (C) 2014-2020 Free Software Foundation, Inc.
-
-;; Author: Thien-Thi Nguyen <ttn@gnu.org>
-;; Maintainer: Thien-Thi Nguyen <ttn@gnu.org>
-;; Version: 3.1.1
-;; Package-Requires: ((ascii-art-to-unicode "1.5") (xpm "1.0.1") (cl-lib
"0.5"))
-;; Keywords: games, processes
-;; URL: http://www.gnuvola.org/software/gnugo/
-
-;; This program is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; This program is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; Playing
-;; -------
-;;
-;; This file provides the command `gnugo' which allows you to play the game of
-;; go against the external program "gnugo" (http://www.gnu.org/software/gnugo)
-;; in a dedicated Emacs buffer, or to resume a game in progress. NOTE: In
-;; this file, to avoid confusion w/ elisp vars and funcs, we use the term "GNU
-;; Go" to refer to the process object created by running the external program.
-;;
-;; At the start of a new game, you can pass additional command-line arguments
-;; to GNU Go to specify level, board size, color, komi, handicap, etc. By
-;; default GNU Go plays at level 10, board size 19, color white, and zero for
-;; both komi and handicap.
-;;
-;; To play a stone, move the cursor to the desired vertice and type `SPC' or
-;; `RET'; to pass, `P' (note: uppercase); to quit, `q'; to undo one of your
-;; moves (as well as a possibly intervening move by GNU Go), `u'. To undo
-;; back through an arbitrary stone that you played, place the cursor on a
-;; stone and type `U' (note: uppercase).
-;;
-;; There are a great many other commands. Other keybindings are described in
-;; the `gnugo-board-mode' documentation, which you may view with the command
-;; `describe-mode' (normally `C-h m') in that buffer. The buffer name shows
-;; the last move and who is currently to play. Capture counts and other info
-;; are shown on the mode line immediately following the major mode name.
-;;
-;; While GNU Go is pondering its next move, certain commands that rely on its
-;; assistence will result in a "still waiting" error. Do not be alarmed; that
-;; is normal. When it is your turn again you may retry the command. In the
-;; meantime, you can use Emacs for other tasks, or start an entirely new game
-;; with `C-u M-x gnugo'. (NOTE: A new game will slow down all games. :-)
-;;
-;; If GNU Go should crash during a game the mode line will show "no process".
-;; Please report the event to the GNU Go maintainers so that they can improve
-;; the program.
-;;
-;;
-;; Meta-Playing (aka Customizing)
-;; ------------------------------
-;;
-;; Customization is presently limited to
-;; vars: `gnugo-program'
-;; `gnugo-animation-string'
-;; `gnugo-mode-line'
-;; `gnugo-X-face' `gnugo-O-face' `gnugo-grid-face'
-;; `gnugo-undo-reaction'
-;; `gnugo-xpms' (see also gnugo-imgen.el)
-;; normal hooks: `gnugo-board-mode-hook'
-;; `gnugo-frolic-mode-hook'
-;; `gnugo-start-game-hook'
-;; `gnugo-post-move-hook'
-;; and the keymaps: `gnugo-board-mode-map'
-;; `gnugo-frolic-mode-map'
-;;
-;;
-;; Meta-Meta-Playing (aka Hacking)
-;; -------------------------------
-;;
-;; <http://git.sv.gnu.org/cgit/emacs/elpa.git/tree/packages/gnugo/>
-
-;;; Code:
-
-(require 'cl-lib) ; use the source luke!
-(require 'time-date) ; for `time-subtract'
-
-;;;---------------------------------------------------------------------------
-;;; Political arts
-
-(defconst gnugo-version "3.1.1"
- "Version of gnugo.el currently loaded.
-This follows a MAJOR.MINOR.PATCH scheme.")
-
-;;;---------------------------------------------------------------------------
-;;; Variables for the uninquisitive programmer
-
-(defvar gnugo-program "gnugo"
- "Name of the GNU Go program (executable file).
-\\[gnugo] validates this using `executable-find'.
-This program must accept command line args:
- --mode gtp --quiet
-For more information on GTP and GNU Go, please visit:
-<http://www.gnu.org/software/gnugo>")
-
-(defvar gnugo-start-game-hook nil
- "Normal hook run immediately before the first move of the game.
-To find out who is to move first, use `gnugo-current-player'.
-See also `gnugo-board-mode'.")
-
-(defvar gnugo-post-move-hook nil
- "Normal hook run after a move and before the board is refreshed.
-Initially, when `run-hooks' is called, the current buffer is the GNUGO
-Board buffer of the game. Hook functions that switch buffers must take
-care not to call (directly or indirectly through some other function)
-`gnugo-put' or `gnugo-get' after the switch.")
-
-(defvar gnugo-animation-string
- (let ((jam "*#") (blink " #") (spin "-\\|/") (yada "*-*!"))
- (concat jam jam jam jam jam
- ;; "SECRET MESSAGE HERE"
- blink blink blink blink blink blink blink blink
- ;; Playing go is like fighting ignorance: when you think you have
- ;; surrounded something by knowing it very well it often turns
- ;; out that in the time you spent deepening this understanding,
- ;; other areas of ignorance have surrounded you.
- spin spin spin spin spin spin spin spin spin
- ;; Playing go is not like fighting ignorance: what one person
- ;; knows many people may come to know; knowledge does not build
- ;; solely move by move. Wisdom, on the other hand...
- yada yada yada))
- "String whose individual characters are used for animation.
-Specifically, the commands `gnugo-worm-stones' and `gnugo-dragon-stones'
-render the stones in their respective result groups as the first character
-in the string, then the next, and so on.")
-
-(defvar gnugo-mode-line "~b ~w :~m :~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
- ~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.
-For ~t, the value is a snapshot, use `gnugo-refresh' to update it.")
-
-(defvar gnugo-X-face 'font-lock-string-face
- "Name of face to use for X (black) stones.")
-
-(defvar gnugo-O-face 'font-lock-builtin-face
- "Name of face to use for O (white) stones.")
-
-(defvar gnugo-grid-face 'default
- "Name of face to use for the grid (A B C ... 1 2 3 ...).")
-
-(defvar gnugo-undo-reaction 'play!
- "What to do if undo (or oops) leaves GNU Go to play.
-After `gnugo-undo-one-move', `gnugo-undo-two-moves' or `gnugo-oops',
-when GNU Go is to play, this can be a symbol:
- play -- make GNU Go play (unless in Zombie mode)
- play! -- make GNU Go play unconditionally (traditional behavior)
- zombie -- enable Zombie mode (`gnugo-zombie-mode')
- one-shot -- like `zombie' but valid only for the next move
-Any other value, or (as a special case) for `gnugo-undo-one-move',
-any value other than `zombie', is taken as `one-shot'. Note that
-making GNU Go play will probably result in the recently-liberated
-board position becoming re-occupied.")
-
-(defvar gnugo-xpms nil
- "List of 46 ((TYPE . LOCATION) . XPM-IMAGE) forms.
-XPM-IMAGE is an image as returned by `create-image' with
-inline data (i.e., property :data with string value).
-
-TYPE is a symbol, one of:
- hoshi -- unoccupied position with dot
- empty -- unoccupied position sans dot
- bpmoku, bmoku -- black stone with and sans highlight point
- wpmoku, wmoku -- white stone with and sans highlight point
-
-LOCATION is an integer encoding edge, corner, or center:
- 1 2 3
- 4 5 6
- 7 8 9
-For instance, 4 means \"left edge\", 9 means \"bottom right\".
-
-There is only one location for hoshi: center. The other five
-types each have all possible locations. So (+ 1 (* 9 5)) => 46.
-
-The value can also be a function (satisfying `functionp') that
-takes one arg, the size of the board, and returns the appropriate
-list of forms.")
-
-;;;---------------------------------------------------------------------------
-;;; Variables for the inquisitive programmer
-
-(defconst gnugo-font-lock-keywords
- '(("X" . gnugo-X-face)
- ("O" . gnugo-O-face))
- "Font lock keywords for `gnugo-board-mode'.")
-
-(defvar gnugo-option-history nil
- "History list of options for `gnugo' invocation.")
-
-(defvar gnugo-state nil) ; hint: C-c C-p
-
-(defvar gnugo-btw nil)
-
-;;;---------------------------------------------------------------------------
-;;; Support functions
-
-(defsubst gnugo-aqr (key alist)
- "Essentially: (cdr (assq KEY ALIST))
-This is like Scheme ‘assq-ref’ but with reversed arguments.
-The name was chosen to occupy the same space as \"cdr (assq\":
- (cdr (assq KEY ALIST))
- (gnugo-aqr KEY ALIST)
-to minimize reindentation noise. [Surely Emacs must
-provide something like this, somewhere, by now? --ttn]"
- (cdr (assq key alist)))
-
-(defsubst gnugo--mkht (&rest etc)
- (apply 'make-hash-table :test 'eq etc))
-
-(defsubst gnugo--compare-strings (s1 beg1 s2 beg2)
- (compare-strings s1 beg1 nil s2 beg2 nil))
-
-(defun gnugo-put (key value)
- "Associate move/game/board-specific property KEY with VALUE.
-
-There are many properties, each named by a keyword, that record and control
-how gnugo.el manages each game. Each GNUGO Board buffer has its own set
-of properties, stored in the hash table `gnugo-state'. Here we document
-some of the more stable properties. You may wish to use them as part of
-a `gnugo-post-move-hook' function, for example. Be careful to preserve
-the current buffer as `gnugo-state' is made into a buffer-local variable.
-NOTE: In the following, \"see foo\" actually means \"see foo source or
-you may never really understand to any degree of personal satisfaction\".
-
- :proc -- subprocess named \"gnugo\", \"gnugo<1>\" and so forth
-
- :diamond -- the part of the subprocess name after \"gnugo\", may be \"\"
-
- :game-over -- nil until game over at which time its value is set to the
- alist ((live GROUP ...) (seki GROUP ...) (dead GROUP ...))
-
- :sgf-collection -- after a `loadsgf' command, entire parse tree of file,
- a simple list of one or more gametrees, updated in
- conjunction w/ :sgf-gametree and :monkey
-
- :sgf-gametree -- one of the gametrees in :sgf-collection
-
- :monkey -- vector of two elements:
- MEM, a pointer to one of the branches in the gametree;
- BIDX, the index of the \"current branch\"
-
- :gnugo-color -- either \"black\" or \"white\"
- :user-color
- :last-mover
-
- :last-waiting -- seconds and time value, respectively; see `gnugo-push-move'
- :waiting-start
-
- :black-captures -- these are strings since gnugo.el doesn't do anything
- :white-captures w/ the information besides display it in the mode line
-
- :display-using-images -- XPMs, to be precise; see functions `gnugo-yy',
- `gnugo-image-display-mode' and `gnugo-refresh',
- as well as gnugo-xpms.el (available elsewhere)
-
- :all-yy -- list of 46 symbols used as the `category' text property
- (so that their plists, typically w/ property `display' or
- `do-not-display') are consulted by the Emacs display engine;
- 46 = 9 places * (4 moku + 1 empty) + 1 hoshi; see functions
- `gnugo-image-display-mode', `gnugo-yy' and `gnugo-yang'
-
- :paren-ov -- a pair (left and right) of overlays shuffled about to indicate
- the last move; only one is used when displaying using images
-
- :last-user-bpos -- board position; keep the hapless human happy
-
-As things stabilize probably more info will be added to this docstring."
- (declare (indent 1))
- (puthash key value gnugo-state))
-
-(defun gnugo-get (key)
- "Return the move/game/board-specific value for KEY.
-See `gnugo-put'."
- (gethash key gnugo-state))
-
-(defun gnugo--forget (&rest keys)
- (dolist (key keys)
- (remhash key gnugo-state)))
-
-(defsubst gnugo--tree-mnum (tree)
- (aref tree 1))
-
-(defsubst gnugo--tree-ends (tree)
- (aref tree 0))
-
-(defsubst gnugo--set-tree-ends (tree ls)
- (aset tree 0 (apply 'vector ls))
- (gnugo--tree-ends tree))
-
-(defun gnugo--root-node (&optional tree)
- (aref (or tree (gnugo-get :sgf-gametree))
- 2))
-
-(defun gnugo-describe-internal-properties ()
- "Pretty-print `gnugo-state' properties in another buffer.
-Handle the big, slow-to-render, and/or uninteresting ones specially."
- (interactive)
- (let ((buf (current-buffer))
- (d (gnugo-get :diamond))
- (acc (cl-loop
- for key being the hash-keys of gnugo-state
- using (hash-values val)
- collect (cons key
- (cl-case key
- ((:xpms)
- (format "hash: %X (%d images)"
- (sxhash val)
- (length val)))
- (:sgf-collection
- (length val))
- (:sgf-gametree
- (list (hash-table-count
- (gnugo--tree-mnum val))
- (gnugo--root-node val)
- (gnugo--tree-ends val)))
- (:monkey
- (let ((mem (aref val 0)))
- (list (aref val 1)
- (car mem))))
- (t val))))))
- (switch-to-buffer (get-buffer-create
- (format "%s*GNUGO Board Properties*"
- d)))
- (erase-buffer)
- (emacs-lisp-mode)
- (setq truncate-lines t)
- (insert ";;; " (message "%d properties" (length acc)))
- (save-excursion
- (cl-loop
- with standard-output = (current-buffer)
- for (key . val) in acc
- do (progn
- (unless (bolp)
- (newline))
- (print key)
- (pp val)))
- (goto-char (point-min))
- (let ((rx (format "overlay from \\([0-9]+\\).+\n%s\\s-+"
- (if (string= "" d)
- ".+\n"
- ""))))
- (while (re-search-forward rx nil t)
- (let ((pos (get-text-property (string-to-number (match-string 1))
- 'gnugo-position buf)))
- (delete-region (+ 2 (match-beginning 0)) (point))
- (insert (format " %S" pos))))))))
-
-(defun gnugo-board-buffer-p (&optional buffer)
- "Return non-nil if BUFFER is a GNUGO Board buffer."
- (eq 'gnugo-board-mode
- (buffer-local-value
- 'major-mode
- (or buffer (current-buffer)))))
-
-(defun gnugo-board-user-play-ok-p (&optional buffer)
- "Return non-nil if BUFFER is a GNUGO Board buffer ready for a user move."
- (with-current-buffer (or buffer (current-buffer))
- (and gnugo-state (not (gnugo-get :waiting)))))
-
-(defsubst gnugo--prop-blackp (object)
- (eq :B object))
-
-(defsubst gnugo--blackp (string)
- (string= "black" string))
-
-(defun gnugo-other (color)
- "If COLOR is \"black\", return \"white\", otherwise \"black\"."
- (if (gnugo--blackp color) "white" "black"))
-
-(defun gnugo-current-player ()
- "Return the current player, either \"black\" or \"white\"."
- (gnugo-other (gnugo-get :last-mover)))
-
-(defsubst gnugo--prop<-color (color)
- (if (gnugo--blackp color) :B :W))
-
-(defun gnugo-gate (&optional in-progress-p)
- (unless (gnugo-board-buffer-p)
- (user-error "Wrong buffer -- try M-x gnugo"))
- (unless (gnugo-get :proc)
- (user-error "No \"gnugo\" process!"))
- (cl-destructuring-bind (&optional color . suggestion)
- (gnugo-get :waiting)
- (when color
- (apply 'user-error
- "%s -- please wait for \"(%s to play)\""
- (if suggestion
- (list "Still thinking"
- color)
- (list "Not your turn yet"
- (gnugo-other color))))))
- (when (and in-progress-p (gnugo-get :game-over))
- (user-error "Sorry, game over")))
-
-(defun gnugo-sentinel (proc string)
- (let ((status (process-status proc)))
- (when (memq status '(exit signal))
- (let ((buf (process-buffer proc)))
- (when (buffer-live-p buf)
- (with-current-buffer buf
- (setq mode-line-process
- (list " [%s ("
- (propertize (car (split-string string))
- 'face 'font-lock-warning-face)
- ")]"))
- (when (eq proc (gnugo-get :proc))
- (gnugo--forget :proc))))))))
-
-(defun gnugo--begin-exchange (proc filter line)
- (declare (indent 2)) ; good time, for a rime
- ; nice style, for a wile...
- (set-process-filter proc filter)
- (process-send-string proc line)
- (process-send-string proc "\n"))
-
-(defun gnugo--q (fmt &rest args)
- "Send formatted command \"FMT ARGS...\"; wait for / return response.
-The response is a string whose first two characters indicate the
-status of the command. See also `gnugo-query'."
- (let ((slow (gnugo-get :waiting))
- (proc (gnugo-get :proc)))
- (when slow
- (user-error "Sorry, still waiting for %s to %s"
- (car slow) (if (cdr slow)
- "receive a suggestion"
- "play")))
- (process-put proc :incomplete t)
- (process-put proc :srs "") ; synchronous return stash
- (gnugo--begin-exchange
- proc (lambda (proc string)
- (let ((full (concat (process-get proc :srs)
- string)))
- (process-put proc :srs full)
- (unless (numberp (gnugo--compare-strings
- full (max 0 (- (length full)
- 2))
- "\n\n" nil))
- (process-put proc :incomplete nil))))
- (if (null args)
- fmt
- (apply #'format fmt args)))
- (while (process-get proc :incomplete)
- (accept-process-output proc 30))
- (prog1 (substring (process-get proc :srs) 0 -2)
- (process-put proc :srs ""))))
-
-(defsubst gnugo--no-worries (string)
- (= ?= (aref string 0)))
-
-(defun gnugo--q/ue (fmt &rest args)
- (let ((ans (apply 'gnugo--q fmt args)))
- (unless (gnugo--no-worries ans)
- (user-error "%s" ans))
- (substring ans 2)))
-
-(defun gnugo-query (message-format &rest args)
- "Send GNU Go a command formatted with MESSAGE-FORMAT and ARGS.
-Return a string that omits the first two characters (corresponding
-to the status indicator in the Go Text Protocol). Use this function
-when you are sure the command cannot fail."
- (substring (apply 'gnugo--q message-format args)
- 2))
-
-(defun gnugo--nquery (cmd)
- (string-to-number (gnugo-query cmd)))
-
-(defun gnugo-lsquery (message-format &rest args)
- "Apply `gnugo-query' to args; split its rv (return list of strings)."
- (split-string (apply 'gnugo-query message-format args)))
-
-(defsubst gnugo--count-query (fmt &rest args)
- (length (apply 'gnugo-lsquery fmt args)))
-
-(defsubst gnugo--root-prop (prop &optional tree)
- (gnugo-aqr prop (gnugo--root-node tree)))
-
-(defun gnugo--set-root-prop (prop value &optional tree)
- (let* ((root (gnugo--root-node tree))
- (cur (assq prop root)))
- (if cur
- (setcdr cur value)
- (push (cons prop value)
- (cdr (last root))))))
-
-(defun gnugo-goto-pos (pos)
- "Move point to board position POS, a letter-number string.
-Return final buffer position (i.e., point)."
- (goto-char (point-min))
- (forward-line (- (1+ (gnugo-get :SZ))
- (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))))
- (point))
-
-(defun gnugo-f (id)
- (intern (if (symbolp id)
- (symbol-name id)
- id)
- (gnugo-get :obarray)))
-
-(defun gnugo-yang (c)
- "Return the \"image type information\" corresponding to character C.
-C is one of the four characters used in the ASCII representation
-of a game board -- ?+ (U+2B PLUS SIGN), ?. (U+2E FULL STOP), ?X
-and ?O (U+58 and U+4F, LATIN CAPITAL LETTER X and O, respectively).
-For example, here is a 5x5 board with two stones placed:
-
- . . . . .
- . O . + . (white at B4)
- . . + . .
- . + . + X (black at E2)
- . . . . .
-
-The image type information consists of a single symbol for ?. and ?+
-and a pair (SANS-POINT . WITH-POINT) for ?X and ?O. Both SANS-POINT
-and WITH-POINT are symbols. For other C, return nil."
- (gnugo-aqr c '((?+ . hoshi)
- (?. . empty)
- (?X . (bmoku . bpmoku))
- (?O . (wmoku . wpmoku)))))
-
-(defun gnugo-yy (yin yang &optional momentaryp)
- "Return a symbol made by formatting YIN (an integer) and YANG.
-The returned symbol has the format N-SYMBOL.
-
-If YANG is a symbol, use it directly. Otherwise, YANG must be a pair.
-If optional arg MOMENTARYP is non-nil, use the `cdr' of YANG.
-Otherwise, use the `car' of YANG. See `gnugo-yang'."
- (gnugo-f (format "%d-%s"
- yin (cond ((symbolp yang) yang)
- (momentaryp (cdr yang))
- (t (car yang))))))
-
-(define-minor-mode gnugo-grid-mode
- "If enabled, display grid around the board."
- :variable
- ((not (memq :nogrid buffer-invisibility-spec))
- .
- (lambda (bool)
- (funcall (if bool
- 'remove-from-invisibility-spec
- 'add-to-invisibility-spec)
- :nogrid)
- (save-excursion (gnugo-refresh)))))
-
-(defconst gnugo--intangible (if (fboundp 'cursor-intangible-mode)
- 'cursor-intangible
- 'intangible)
- "Text property that controls intangibility.")
-
-(defun gnugo--propertize-board-buffer ()
- (erase-buffer)
- (insert (substring (gnugo--q "showboard") 3))
- (let* ((grid-props (list 'invisible :nogrid
- 'font-lock-face gnugo-grid-face))
- (%gpad (gnugo-f 'gpad))
- (%gspc (gnugo-f 'gspc))
- (%lpad (gnugo-f 'lpad))
- (%rpad (gnugo-f 'rpad))
- (ispc-props (list 'category (gnugo-f 'ispc) 'rear-nonsticky t))
- (size (gnugo-get :SZ))
- (size-string (number-to-string size)))
- (goto-char (point-min))
- (put-text-property (point) (1+ (point)) 'category (gnugo-f 'tpad))
- (skip-chars-forward " ")
- (put-text-property (1- (point)) (point) 'category %gpad)
- (put-text-property (point) (line-end-position) 'category %gspc)
- (forward-line 1)
- (add-text-properties (1+ (point-min)) (1- (point)) grid-props)
- (while (looking-at "\\s-*\\([0-9]+\\)[ ]")
- (let* ((row (match-string-no-properties 1))
- (edge (match-end 0))
- (other-edge (+ edge (* 2 size) -1))
- (right-empty (+ other-edge (length row) 1))
- (top-p (string= size-string row))
- (bot-p (string= "1" row)))
- (let* ((nL (- edge 1 (length size-string)))
- (nR (- edge 1))
- (ov (make-overlay nL nR (current-buffer) t)))
- (add-text-properties nL nR grid-props)
- ;; We redundantly set `invisible' in the overlay to workaround
- ;; a display bug whereby text *following* the overlaid text is
- ;; displayed with the face of the overlaid text, but only when
- ;; that text is invisible (i.e., `:nogrid' in invisibility spec).
- ;; This has something to do w/ the bletcherous `before-string'.
- (overlay-put ov 'invisible :nogrid)
- (overlay-put ov 'category %lpad))
- (cl-do ((p edge (+ 2 p)) (ival 'even (if (eq 'even ival) 'odd 'even)))
- ((< other-edge p))
- (let* ((position (format "%c%s" (aref "ABCDEFGHJKLMNOPQRST"
- (truncate (- p edge) 2))
- row))
- (yin (let ((A-p (= edge p))
- (Z-p (= (1- other-edge) p)))
- (cond ((and top-p A-p) 1)
- ((and top-p Z-p) 3)
- ((and bot-p A-p) 7)
- ((and bot-p Z-p) 9)
- (top-p 2)
- (bot-p 8)
- (A-p 4)
- (Z-p 6)
- (t 5))))
- (yang (gnugo-yang (char-after p))))
- (add-text-properties p (1+ p)
- `(gnugo-position
- ,position
- gnugo-yin
- ,yin
- gnugo-yang
- ,yang
- category
- ,(gnugo-yy yin yang)
- front-sticky
- (gnugo-position gnugo-yin))))
- (unless (= (1- other-edge) p)
- (add-text-properties (1+ p) (+ 2 p) ispc-props)
- (put-text-property p (+ 2 p) gnugo--intangible ival)))
- (add-text-properties (1+ other-edge) right-empty grid-props)
- (goto-char right-empty)
- (when (looking-at "\\s-+\\(WH\\|BL\\).*capt.* \\([0-9]+\\).*$")
- (let ((prop (if (string= "WH" (match-string 1))
- :white-captures
- :black-captures))
- (beg (match-beginning 2))
- (end (match-end 2)))
- (put-text-property beg end :gnugo-cf (cons (- end beg) prop))
- (gnugo-put prop (match-string-no-properties 2))))
- (put-text-property right-empty (line-end-position) 'category %rpad)
- (forward-line 1)))
- (add-text-properties (1- (point)) (point-max) grid-props)
- (skip-chars-forward " ")
- (put-text-property (1- (point)) (point) 'category %gpad)
- (put-text-property (point) (line-end-position)
- 'category %gspc)))
-
-(defun gnugo--merge-showboard-results ()
- (let ((aft (substring (gnugo--q "showboard") 3))
- (adj 1) ; string to buffer position adjustment
-
- (sync "[0-9]* stones$")
- ;; Note: `sync' used to start w/ "[0-9]+", but that is too
- ;; restrictive a condition that fails in the case of:
- ;;
- ;; (before)
- ;; ... WHITE has captured 1 stones
- ;; ^
- ;; (after)
- ;; ... WHITE has captured 14 stones
- ;; ^
- ;;
- ;; where the after count has more digits than the before count,
- ;; but shares the same leading digits. In this case, the result
- ;; of `compare-strings' points to the SPC following the before
- ;; count (indicated by caret in this example).
-
- (bef (buffer-substring-no-properties (point-min) (point-max)))
- (bef-start 0) (bef-idx 0)
- (aft-start 0) (aft-idx 0)
- aft-sync-backtrack mis inc cut new very-strange
-
- (inhibit-read-only t))
- (while (numberp (setq mis (gnugo--compare-strings
- bef bef-start
- aft aft-start)))
- (setq aft-sync-backtrack nil
- inc (if (cl-minusp mis)
- (- (+ 1 mis))
- (- mis 1))
- bef-idx (+ bef-start inc)
- aft-idx (+ aft-start inc)
- bef-start (if (eq bef-idx (string-match sync bef bef-idx))
- (match-end 0)
- (1+ bef-idx))
- aft-start (if (and (eq aft-idx (string-match sync aft aft-idx))
- (let ((peek (1- aft-idx)))
- (while (not (= 32 (aref aft peek)))
- (setq peek (1- peek)))
- (setq aft-sync-backtrack (1+ peek))))
- (match-end 0)
- (1+ aft-idx))
- cut (+ bef-idx adj
- (if aft-sync-backtrack
- (- aft-sync-backtrack aft-idx)
- 0)))
- (goto-char cut)
- (if aft-sync-backtrack
- (let* ((asb aft-sync-backtrack)
- (l-p (get-text-property cut :gnugo-cf))
- (old-len (car l-p))
- (capprop (cdr l-p))
- (keep (text-properties-at cut)))
- (setq new (substring aft asb (string-match " " aft asb)))
- (plist-put keep :gnugo-cf (cons (length new) capprop))
- (gnugo-put capprop new)
- (delete-char old-len)
- (insert (apply 'propertize new keep))
- (cl-incf adj (- (length new) old-len)))
- (setq new (aref aft aft-idx))
- (insert-and-inherit (char-to-string new))
- (let ((yin (get-text-property cut 'gnugo-yin))
- (yang (gnugo-yang new)))
- (add-text-properties cut (1+ cut)
- `(gnugo-yang
- ,yang
- category
- ,(gnugo-yy yin yang))))
- (delete-char 1)
- ;; Do this last to avoid complications w/ font lock and overlays
- ;; (this also means we cannot include `intangible' in `front-sticky').
- ;; This is necessary even for ‘cursor-intangible’; if we omit it, the
- ;; cursor can (incorrectly) enter the text displayed by ‘:paren-ov’.
- ;; TODO: Revisit later to see if that still holds.
- (when (setq very-strange (get-text-property (1+ cut)
gnugo--intangible))
- (put-text-property cut (1+ cut) gnugo--intangible very-strange))))))
-
-(defsubst gnugo--move-prop (node)
- (or (assq :B node)
- (assq :W node)))
-
-(defun gnugo--as-pos-func ()
- (let ((size (gnugo-get :SZ)))
- ;; rv
- (lambda (cc)
- (if (string= "" cc)
- "PASS"
- (let ((col (aref cc 0)))
- (format "%c%d"
- (+ ?A (- (if (> ?i col) col (1+ col)) ?a))
- (- size (- (aref cc 1) ?a))))))))
-
-(defsubst gnugo--resignp (string)
- (string= "resign" string))
-
-(defsubst gnugo--passp (string)
- (string= "PASS" string))
-
-(defun gnugo-move-history (&optional rsel color)
- "Determine and return the game's move history.
-Optional arg RSEL controls side effects and return value.
-If nil, display the history in the echo area as \"(N moves)\"
-followed by the space-separated list of moves. When called
-interactively with a prefix arg (i.e., RSEL is (4)), display
-similarly, but suffix with the mover (either \":B\" or \":W\").
-RSEL may also be a symbol that selects what to return:
- car -- the most-recent move
- cadr -- the next-to-most-recent move
- two -- the last two moves as a list, oldest last
- bpos -- the last stone on the board placed by COLOR
-For all other values of RSEL, do nothing and return nil."
- (interactive "P")
- (let* ((monkey (gnugo-get :monkey))
- (mem (aref monkey 0))
- (as-pos (gnugo--as-pos-func))
- acc node mprop move)
- (cl-flet*
- ((as-pos-maybe (x) (if (gnugo--resignp x)
- x
- (funcall as-pos x)))
- (remem () (setq node (pop mem)
- mprop (gnugo--move-prop node)))
- (next (byp) (when (remem)
- (setq move (as-pos-maybe (cdr mprop)))
- (push (if byp
- (format "%s%s" move (car mprop))
- move)
- acc)))
- (nn () (next nil))
- (tell () (message "(%d moves) %s"
- (length acc)
- (mapconcat 'identity (nreverse acc) " ")))
- (finish (byp) (while mem (next byp)) (tell)))
- (pcase rsel
- (`(4) (finish t))
- (`nil (finish nil))
- (`car (car (nn)))
- (`cadr (nn) (car (nn)))
- (`two (nn) (nn) acc)
- (`bpos (cl-loop
- with prop = (gnugo--prop<-color color)
- while mem
- when (and (remem)
- (eq prop (car mprop))
- (setq move (cdr mprop))
- ;; i.e., "normal CC" position
- (= 2 (length move)))
- return (funcall as-pos move)))
- (_ nil)))))
-
-(defun gnugo-boss-is-near ()
- "Do `bury-buffer' until the current one is not a GNU Board."
- (interactive)
- (while (gnugo-board-buffer-p)
- (bury-buffer)))
-
-(defsubst gnugo--no-regrets (monkey ends)
- (eq (aref ends (aref monkey 1))
- (aref monkey 0)))
-
-(defun gnugo--as-cc-func ()
- (let ((size (gnugo-get :SZ)))
- (lambda (pos)
- (let* ((col (aref pos 0))
- (one (+ ?a (- col (if (< ?H col) 1 0) ?A)))
- (two (+ ?a (- size (string-to-number
- (substring pos 1))))))
- (format "%c%c" one two)))))
-
-(defun gnugo--decorate (node &rest plist)
- (cl-loop
- with tp = (last node)
- with fruit
- while plist
- do (setf
- fruit (list
- ;; No OoE worries, here. "The first step in evaluating a
- ;; function call is to evaluate the remaining elements of the
- ;; list from left to right." (info "(elisp) Function Forms")
- (cons
- (pop plist)
- (pop plist)))
- (cdr tp) fruit
- tp fruit)))
-
-(defun gnugo-close-game (end-time resign)
- (gnugo-put :game-end-time end-time)
- (let ((now (or end-time (current-time))))
- (gnugo-put :scoring-seed (logior (ash (logand (car now) 255) 16)
- (cadr now))))
- (gnugo-put :game-over
- (cl-flet
- ;; Q: What form does a game-over "group" take?
- ;; A: GROUP = (GHEAD POSITION[...])
- ;; GHEAD = (CPROP [OVERLAY[...]])
- ;; CPROP = ‘:B’ or ‘:W’
- ((group (color positions)
- (cl-assert positions) ; one or more
- (cons (list (gnugo--prop<-color color))
- (sort positions #'string<))))
- (if (or (eq t resign)
- (and (stringp resign)
- (string-match "[BW][+][Rr]esign" resign)))
- ;; Hmmm, treating resignation specially seems kind of pointless.
- ;; TODO: Choose one: (a) rationalize; (b) decruft.
- `((live ,@(cl-flet
- ((ls (color) (mapcar
- (lambda (x)
- (group color (split-string x)))
- (split-string
- (gnugo-query "worm_stones %s" color)
- "\n"))))
- (append (ls "black")
- (ls "white"))))
- (seki)
- (dead))
- (cl-loop
- with flat-seki = (gnugo-lsquery "final_status_list seki")
- with dd = (gnugo-query "dragon_data")
- with start = 0
- with (live seki dead)
- while (string-match "\\(.+\\):\n[^ ]+[ ]+\\(black\\|white\\)\n"
- dd start)
- do (let* ((mem (match-string 1 dd))
- (ent (group (match-string 2 dd)
- (gnugo-lsquery "dragon_stones %s"
- mem))))
- (string-match "\nstatus[ ]+\\(\\(ALIVE\\)\\|[A-Z]+\\)\n"
- dd start)
- (cond ((member mem flat-seki)
- (push ent seki))
- ((match-string 2 dd)
- (push ent live))
- (t
- (push ent dead)))
- (setq start (match-end 0)))
- finally return
- `((live ,@live)
- (seki ,@seki)
- (dead ,@dead)))))))
-
-(defun gnugo--unclose-game ()
- (gnugo--forget :game-over ; all those in -close-game
- :scoring-seed
- :game-end-time)
- (let* ((root (gnugo--root-node))
- (cur (assq :RE root)))
- (when cur
- (cl-assert (not (eq cur (car root))) nil
- ":RE at head of root node: %S"
- root)
- (delq cur root))))
-
-(defun gnugo-push-move (who move)
- (let* ((simple (booleanp who))
- (ucolor (gnugo-get :user-color))
- (color (if simple
- (if who
- ucolor
- (gnugo-get :gnugo-color))
- who))
- (start (gnugo-get :waiting-start))
- (now (current-time))
- (resignp (gnugo--resignp move))
- (passp (gnugo--passp move))
- (head (gnugo-move-history 'car))
- (onep (and head (gnugo--passp head)))
- (donep (or resignp (and onep passp))))
- (unless resignp
- (gnugo--q/ue "play %s %s" color move))
- (unless passp
- (gnugo--merge-showboard-results))
- (gnugo-put :last-mover color)
- (when (if simple
- who
- (string= ucolor color))
- (gnugo-put :last-user-bpos (and (not passp) (not resignp) move)))
- ;; update :sgf-gametree and :monkey
- (let* ((property (gnugo--prop<-color color))
- (pair (cons property (cond (resignp move)
- (passp "")
- (t (funcall (gnugo--as-cc-func)
- move)))))
- (fruit (list pair))
- (monkey (gnugo-get :monkey))
- (mem (aref monkey 0))
- (tip (car mem))
- (tree (gnugo-get :sgf-gametree))
- (ends (gnugo--tree-ends tree))
- (mnum (gnugo--tree-mnum tree))
- (count (length ends))
- (tip-move-num (gethash tip mnum))
- (bidx (aref monkey 1)))
- ;; Detect déjà-vu. That is, when placing "A", avoid:
- ;;
- ;; X---Y---A new
- ;; \
- ;; --A---B old
- ;;
- ;; (such "variations" do not actually vary!) in favor of:
- ;;
- ;; X---Y---A new
- ;; \
- ;; --B old
- ;;
- ;; This linear search loses for multiple ‘old’ w/ "A",
- ;; a very unusual (but not invalid, sigh) situation.
- (cl-loop
- with (bx previous)
- for i
- ;; Start with latest / highest likelihood for hit.
- ;; (See "to the right" comment, below.)
- from (if (gnugo--no-regrets monkey ends)
- 1
- 0)
- below count
- if (setq bx (mod (+ bidx i) count)
- previous
- (cl-loop
- with node
- for m on (aref ends bx)
- while (< tip-move-num
- (gethash (setq node (car m))
- mnum))
- if (eq mem (cdr m))
- return (when (equal pair (assq property node))
- m)
- finally return nil))
- ;; yes => follow
- return
- (progn
- (unless (= bidx bx)
- (cl-rotatef (aref ends bidx)
- (aref ends bx)))
- (setq mem previous))
- ;; no => construct
- finally do
- (progn
- (unless (gnugo--no-regrets monkey ends)
- (setq ends (gnugo--set-tree-ends
- tree (let ((ls (append ends nil)))
- ;; copy old to the right of new
- (push mem (nthcdr bidx ls))
- ls))))
- (puthash fruit (1+ (gethash tip mnum)) mnum)
- (push fruit mem)
- (aset ends bidx mem)))
- (setf (aref monkey 0) mem))
- (when start
- (gnugo-put :last-waiting (cadr (time-subtract now start))))
- (when donep
- (gnugo-close-game now resignp))
- (gnugo-put :waiting-start (and (not donep) now))
- donep))
-
-(defun gnugo-venerate (yin yang)
- (let* ((fg-yy (gnugo-yy yin yang))
- (fg-disp (or (get fg-yy 'display)
- (get fg-yy 'do-not-display)))
- (fg-props (cdr fg-disp))
- (fg-data (plist-get fg-props :data))
- (c-symbs (plist-get fg-props :color-symbols))
- (bg-yy (gnugo-yy yin (gnugo-yang ?.)))
- (bg-disp (or (get bg-yy 'display)
- (get bg-yy 'do-not-display)))
- (bg-data (plist-get (cdr bg-disp) :data))
- (bop (lambda (s)
- (let* ((start 0)
- (ncolors
- (when (string-match "\\([0-9]+\\)\\s-+[0-9]+\"," s)
- (setq start (match-end 0))
- (string-to-number (match-string 1 s)))))
- (while (and (not (cl-minusp ncolors))
- (string-match ",\n" s start))
- (setq start (match-end 0)
- ncolors (1- ncolors)))
- (string-match "\"" s start)
- (match-end 0))))
- (new (copy-sequence fg-data))
- (lx (length fg-data))
- (sx (funcall bop fg-data))
- (sb (funcall bop bg-data))
- (color-key (aref new sx))) ; blech, heuristic
- (while (< sx lx)
- (when (and (not (= color-key (aref new sx)))
- (cl-plusp (random 4)))
- (aset new sx (aref bg-data sb)))
- (cl-incf sx)
- (cl-incf sb))
- (apply 'create-image new 'xpm t
- :ascent 'center (when c-symbs
- (list :color-symbols
- c-symbs)))))
-
-(defsubst gnugo--zonk-ovs (ovs)
- (mapc 'delete-overlay ovs))
-
-(defun gnugo-refresh (&optional nocache)
- "Update GNUGO Board buffer display.
-While a game is in progress, parenthesize the last-played stone (no parens
-for pass). If the buffer is currently displayed in the selected window,
-recenter the board (presuming there is extra space in the window). Update
-the mode line. Lastly, move point to the last position played by the user,
-if that move was not a pass.
-
-Prefix arg NOCACHE requests complete reconstruction of the display, which may
-be slow. (This should normally be unnecessary; specify it only if the display
-seems corrupted.) NOCACHE is silently ignored when GNU Go is thinking about
-its move."
- (interactive "P")
- (let* ((move (gnugo-move-history 'car))
- (game-over (gnugo-get :game-over))
- (using-images (gnugo-get :display-using-images))
- (inhibit-read-only t)
- window last)
- (when (and nocache (not (gnugo-get :waiting)))
- ;; (search-forward "pall of death")
- (dolist (group (apply #'append (mapcar #'cdr game-over)))
- (gnugo--zonk-ovs (cdar group))
- (setcdr (car group) nil))
- (gnugo--propertize-board-buffer))
- ;; last move
- (when move
- (cl-destructuring-bind (l-ov . r-ov) (gnugo-get :paren-ov)
- (if (member move '("PASS" "resign"))
- (gnugo--zonk-ovs (list l-ov r-ov))
- (let* ((p (gnugo-goto-pos move))
- (hspec (gnugo-get :highlight-last-move-spec))
- (display-value (nth 0 hspec))
- (l-offset (nth 1 hspec))
- (l-new-pos (+ p l-offset))
- (r-action (nth 2 hspec)))
- (overlay-put l-ov 'display
- (if (functionp display-value)
- (funcall display-value p)
- display-value))
- (move-overlay l-ov l-new-pos (1+ l-new-pos))
- (if r-action
- (funcall r-action r-ov)
- (move-overlay r-ov (+ l-new-pos 2) (+ l-new-pos 3)))))))
- ;; buffer name
- (rename-buffer (concat (gnugo-get :diamond)
- (if game-over
- (format "%s(game over)"
- (if (gnugo--resignp move)
- (concat move "ation ")
- ""))
- (format "%s(%s to play)"
- (if move (concat move " ") "")
- (gnugo-current-player)))))
- ;; pall of death
- (when game-over
- (cl-destructuring-bind (live seki dead)
- (mapcar (lambda (sel)
- (gnugo-aqr sel game-over))
- '(live seki dead))
- (dolist (head (mapcar #'car live))
- (gnugo--zonk-ovs (cdr head))
- (setcdr head nil))
- (cl-flet
- ((P (groups face respect)
- (cl-loop
- for (head . positions) in groups
- unless (cdr head)
- do (setcdr
- head
- (cl-loop
- with b/w = (gnugo--prop-blackp (car head))
- with yang = (if using-images
- (gnugo-yang (if b/w ?X ?O))
- (propertize (if b/w "x" "o")
- 'face face))
- for pos in (mapcar #'gnugo-goto-pos positions)
- collect
- (let ((ov (make-overlay pos (1+ pos))))
- (overlay-put
- ov 'display
- (if using-images
- ;; respect the dead individually; it takes more
- ;; time but that's not a problem (for them)
- (funcall respect (get-text-property
- pos 'gnugo-yin)
- yang)
- yang))
- ov))))))
- (P seki 'font-lock-type-face
- (lambda (yin yang)
- (get (gnugo-yy yin yang t)
- 'display)))
- (P dead 'font-lock-warning-face
- #'gnugo-venerate))))
- ;; window update
- (when (setq window (get-buffer-window (current-buffer)))
- (let* ((gridp (not (memq :nogrid buffer-invisibility-spec)))
- (size (gnugo-get :SZ))
- (under10p (< size 10))
- (mul (gnugo-get :mul))
- (h (- (truncate (- (window-height window)
- (* size (cdr mul))
- (if gridp 2 0))
- 2)
- (if gridp 0 1)))
- (edges (window-edges window))
- (right-w-edge (nth 2 edges))
- (avail-width (- right-w-edge (nth 0 edges)))
- (wmul (car mul))
- (imagesp (symbol-plist (gnugo-f 'ispc)))
- (w (/ (- avail-width
- (* size wmul)
- (if imagesp
- 0
- (1- size))
- 2 ; between board and grid
- (if gridp
- (if under10p 2 4)
- 0))
- 2.0)))
- (dolist (pair `((tpad . ,(if (and h (cl-plusp h))
- `(display ,(make-string h 10))
- '(invisible :nogrid)))
- (gpad . (display
- (space :align-to
- ,(+ w
- 2.0
- (cond (imagesp (+ (* 0.5 wmul)
- (if under10p
- -0.5
- 0.5)))
- (under10p 0)
- (t 1))))))
- (gspc . ,(when imagesp
- `(display
- (space-width
- ,(-
- ;; DWR: image width alone => OBOE!
- ;;- wmul
- ;; NB: ‘(* wmul cw)’ is the same
- ;; as ‘(car (image-size ... t))’.
- (let ((cw (frame-char-width)))
- (/ (+ 1.0 (* wmul cw))
- cw))
- 1.0)))))
- (lpad . ,(let ((d `(display (space :align-to ,w))))
- ;; We distinguish between these cases to
- ;; workaround a display bug whereby the
- ;; `before-string' is omitted entirely (not
- ;; rendered) when interacting w/ the text
- ;; mode last-move left-paren for moves in
- ;; column A.
- (if gridp
- `(before-string
- ,(apply 'propertize " " d))
- d)))
- (rpad . (display
- (space :align-to ,(1- avail-width))))))
- (setplist (gnugo-f (car pair)) (cdr pair)))))
- ;; mode line update
- (let ((cur (gnugo-get :mode-line)))
- (unless (equal cur gnugo-mode-line)
- (setq cur gnugo-mode-line)
- (gnugo-put :mode-line cur)
- (gnugo-put :mode-line-form
- (if (consp cur)
- cur
- (let (v refs varlist)
- (cl-flet
- ((R (re rep)
- (setq cur (replace-regexp-in-string
- re rep cur t t))))
- (R "%" "%%") ; hygiene
- (R "~[bwpmtu]"
- (lambda (match)
- (prog1 "%s"
- (push (setq v (intern match))
- refs)
- (cl-pushnew
- (list
- v
- (cl-case v
- (~b '(or (gnugo-get :black-captures) 0))
- (~w '(or (gnugo-get :white-captures) 0))
- (~p '(gnugo-current-player))
- (~t '(let ((ws (gnugo-get :waiting-start)))
- (if ws
- (cadr (time-since ws))
- "-")))
- (~u '(or (gnugo-get :last-waiting) "-"))
- (~m '(let ((tree (gnugo-get :sgf-gametree))
- (monkey (gnugo-get :monkey)))
- (gethash (car (aref monkey 0))
- (gnugo--tree-mnum tree)
- ;; should be unnecessary
- "?")))))
- varlist :key 'car)))))
- `(let ,varlist
- (format ,cur ,@(nreverse refs)))))))
- (let ((form (gnugo-get :mode-line-form)))
- (setq mode-line-process
- (and form
- ;; this dynamicism is nice but excessive in its wantonness
- ;;- `(" [" (:eval ,form) "]")
- ;; this dynamicism is ok because the user triggers it
- (format " [%s]" (eval form)))))
- (force-mode-line-update))
- ;; last user move
- (when (setq last (gnugo-get :last-user-bpos))
- (gnugo-goto-pos last))))
-
-(defun gnugo--turn-the-wheel (&optional now)
- (unless (gnugo-get :waiting)
- (let ((color (gnugo-current-player))
- (wheel (gnugo-get :wheel)))
- (setcar wheel
- (when (and (not (gnugo-get :game-over))
- (member color (cdr wheel)))
- (run-at-time
- (if now
- nil
- 2) ;;; sec (frettoloso? dubioso!)
- nil
- (lambda (buf color wheel)
- (setcar wheel nil)
- (with-current-buffer buf
- (gnugo-get-move color)))
- (current-buffer)
- color wheel))))))
-
-(defun gnugo--finish-move (&optional now)
- (let ((buf (current-buffer)))
- (run-hooks 'gnugo-post-move-hook)
- (set-buffer buf))
- (gnugo-refresh)
- (gnugo--turn-the-wheel now))
-
-;;;---------------------------------------------------------------------------
-;;; Game play actions
-
-(defun gnugo--rename-buffer-portion (&optional back)
- (let ((old "to play")
- (new "waiting for suggestion"))
- (when back
- (cl-rotatef old new))
- (let ((name (buffer-name)))
- (when (string-match old name)
- (rename-buffer (replace-match new t t name))))))
-
-(defun gnugo--display-suggestion (color suggestion)
- (message "%sSuggestion for %s: %s"
- (gnugo-get :diamond)
- color suggestion))
-
-(defun gnugo-get-move-insertion-filter (proc string)
- (with-current-buffer (process-buffer proc)
- (let* ((so-far (gnugo-get :get-move-string))
- (full (gnugo-put :get-move-string (concat so-far string))))
- (when (string-match "^= \\(.+\\)\n\n" full)
- (setq full (match-string 1 full)) ; POS or "PASS"
- (cl-destructuring-bind (color . suggestion)
- (gnugo-get :waiting)
- (gnugo--forget :get-move-string
- :waiting)
- (if suggestion
- (progn
- (gnugo--rename-buffer-portion t)
- (unless (or (gnugo--passp full)
- (eq 'nowarp suggestion))
- (gnugo-goto-pos full))
- (gnugo--display-suggestion color full))
- (gnugo-push-move color full)
- (gnugo--finish-move)))))))
-
-(defun gnugo-get-move (color &optional suggestion)
- (gnugo-put :waiting (cons color suggestion))
- (gnugo--begin-exchange
- (gnugo-get :proc) 'gnugo-get-move-insertion-filter
- ;; We used to use ‘genmove’ here, but that forced asymmetry in
- ;; downstream handling, an impediment to GNU Go vs GNU Go fun.
- (concat "reg_genmove " color))
- (accept-process-output))
-
-(defun gnugo-cleanup ()
- (when (gnugo-board-buffer-p)
- (unless (zerop (buffer-size))
- (message "Thank you for playing GNU Go."))
- (setq gnugo-state nil)))
-
-(defun gnugo-position ()
- (or (get-text-property (point) 'gnugo-position)
- (user-error "Not a proper position point")))
-
-(defun gnugo-request-suggestion (&optional nowarp)
- "Request a move suggestion from GNU Go.
-After some time (during which you can do other stuff),
-Emacs displays the suggestion in the echo area and warps the
-cursor to the suggested position. Prefix arg inhibits warp."
- (interactive "P")
- (gnugo-gate t)
- (gnugo--rename-buffer-portion)
- (gnugo-get-move (gnugo-current-player)
- (if nowarp
- 'nowarp
- t)))
-
-(defun gnugo--karma (color) ; => BOOL
- (when (member color (cdr (gnugo-get :wheel)))
- t))
-
-(defsubst gnugo--:karma (role)
- (gnugo--karma (gnugo-get role)))
-
-(defun gnugo--assist-state (&optional gate)
- (let ((bool (gnugo--:karma :user-color)))
- (if (and bool gate)
- (user-error "Sorry, Assist mode enabled")
- bool)))
-
-(defun gnugo--user-play (pos-or-pass)
- (gnugo-gate t)
- ;; The "user" in this func's name used to signify both
- ;; who does the action and for whom the action is done.
- ;; Now, it signifies only the former.
- (let ((color (gnugo-current-player)))
- ;; Don't get confused by mixed signals.
- (when (gnugo--karma color)
- (if (equal color (gnugo-get :one-shot))
- (gnugo--forget :one-shot)
- (user-error "Sorry, you cannot play for %s at this time"
- color)))
- (gnugo-push-move color pos-or-pass))
- (gnugo--finish-move t))
-
-(defun gnugo-move ()
- "Make a move on the GNUGO Board buffer.
-The position is computed from current point.
-Signal error if done out-of-turn or if game-over.
-To start a game try M-x gnugo."
- (interactive)
- (gnugo--user-play (gnugo-position)))
-
-(defun gnugo-mouse-move (e)
- "Do `gnugo-move' at mouse location."
- (interactive "@e")
- (mouse-set-point e)
- (when (memq (following-char) '(?. ?+))
- (gnugo-move)))
-
-(defun gnugo-pass ()
- "Make a pass on the GNUGO Board buffer.
-Signal error if done out-of-turn or if game-over.
-To start a game try M-x gnugo."
- (interactive)
- (gnugo--user-play "PASS"))
-
-(defun gnugo-mouse-pass (e)
- "Do `gnugo-pass' at mouse location."
- (interactive "@e")
- (mouse-set-point e)
- (gnugo-pass))
-
-(defun gnugo-resign ()
- (interactive)
- (gnugo-gate t)
- (if (not (y-or-n-p "Resign? "))
- (message "(not resigning)")
- (gnugo-push-move t "resign")
- (gnugo-refresh)))
-
-(defun gnugo-animate-group (w/d)
- ;; W/D is a symbol, either ‘worm’ or ‘dragon’.
- (gnugo-gate)
- (let* ((pos (gnugo-position))
- (orig-b-m-p (buffer-modified-p))
- blurb stones)
- (unless (memq (following-char) '(?X ?O))
- (user-error "No stone at %s" pos))
- (setq blurb (message "Computing %s stones ..." w/d)
- stones (gnugo-lsquery "%s_stones %s" w/d pos))
- (message "%s %s in group" blurb (length stones))
- (setplist (gnugo-f 'anim) nil)
- (let* ((spec (if (gnugo-get :display-using-images)
- (cl-loop
- with yin = (get-text-property (point) 'gnugo-yin)
- with yang = (gnugo-yang (following-char))
- with up = (get (gnugo-yy yin yang t) 'display)
- with dn = (get (gnugo-yy yin yang) 'display)
- for n below (length gnugo-animation-string)
- collect (if (zerop (logand 1 n))
- dn up))
- (split-string gnugo-animation-string "" t)))
- (cell (list spec))
- (ovs (save-excursion
- (mapcar (lambda (pos)
- (let* ((p (gnugo-goto-pos pos))
- (ov (make-overlay p (1+ p))))
- (overlay-put ov 'category (gnugo-f 'anim))
- (overlay-put ov 'priority most-positive-fixnum)
- ov))
- stones))))
- (setplist (gnugo-f 'anim) (cons 'display cell))
- (while (and (cdr spec) ; let last linger lest levity lost
- (sit-for 0.08675309)) ; jenny jenny i got your number...
- (setcar cell (setq spec (cdr spec)))
- ;; Force redisplay of overlays.
- (set-buffer-modified-p orig-b-m-p))
- (sit-for 5)
- (gnugo--zonk-ovs ovs)
- t)))
-
-(defun gnugo-display-group-data (command buffer-name)
- (gnugo-gate)
- (message "Computing %s ..." command)
- (let ((data (gnugo--q "%s %s" command (gnugo-position))))
- (switch-to-buffer buffer-name)
- (erase-buffer)
- (insert data))
- (message "Computing %s ... done" command))
-
-(defun gnugo-worm-stones ()
- "In the GNUGO Board buffer, animate \"worm\" at current position.
-Signal error if done out-of-turn or if game-over.
-See variable `gnugo-animation-string' for customization."
- (interactive)
- (gnugo-animate-group 'worm))
-
-(defun gnugo-worm-data ()
- "Display in another buffer data from \"worm\" at current position.
-Signal error if done out-of-turn or if game-over."
- (interactive)
- (gnugo-display-group-data "worm_data" "*gnugo worm data*"))
-
-(defun gnugo-dragon-stones ()
- "In the GNUGO Board buffer, animate \"dragon\" at current position.
-Signal error if done out-of-turn or if game-over.
-See variable `gnugo-animation-string' for customization."
- (interactive)
- (gnugo-animate-group 'dragon))
-
-(defun gnugo-dragon-data ()
- "Display in another buffer data from \"dragon\" at current position.
-Signal error if done out-of-turn or if game-over."
- (interactive)
- (gnugo-display-group-data "dragon_data" "*gnugo dragon data*"))
-
-(defun gnugo-estimate-score ()
- "Display estimated score of a game of GNU Go.
-Output includes number of stones on the board and number of stones
-captured by each player, and the estimate of who has the advantage (and
-by how many stones)."
- (interactive)
- (message "Est.score ...")
- (let ((black (gnugo--count-query "list_stones black"))
- (white (gnugo--count-query "list_stones white"))
- (black-captures (gnugo-query "captures black"))
- (white-captures (gnugo-query "captures white"))
- (est (gnugo-query "estimate_score")))
- ;; might as well update this
- (gnugo-put :black-captures black-captures)
- (gnugo-put :white-captures white-captures)
- (message "Est.score ... B %s %s | W %s %s | %s"
- black black-captures white white-captures est)))
-
-(defun gnugo--ok-file (filename)
- (setq default-directory
- (file-name-directory
- (expand-file-name filename)))
- (set-buffer-modified-p nil))
-
-(defun gnugo-write-sgf-file (filename)
- "Save the game history to FILENAME (even if unfinished).
-If FILENAME already exists, Emacs confirms that you wish to overwrite it."
- (interactive "FWrite game as SGF file: ")
- (when (and (file-exists-p filename)
- (not (y-or-n-p "File exists. Continue? ")))
- (user-error "Not writing %s" filename))
- (when (buffer-modified-p)
- ;; take responsibility for our actions
- (gnugo--set-root-prop :AP (cons "gnugo.el" gnugo-version)))
- (gnugo/sgf-write-file (gnugo-get :sgf-collection) filename)
- (gnugo--ok-file filename))
-
-(defun gnugo--dance-dance (karma)
- (cl-destructuring-bind (dance btw)
- (aref [(moshpit " Zombie")
- (classic nil)
- (reverse " Zombie Assist") ; "Assist Zombie"? no thanks! :-D
- (stilted " Assist")]
- (cl-flet
- ((try (n prop)
- (if (member (gnugo-get prop)
- karma)
- n
- 0)))
- (+ (try 2 :user-color)
- (try 1 :gnugo-color))))
- (gnugo-put :dance dance) ; pure cruft (for now)
- (setq gnugo-btw btw)))
-
-(defun gnugo--who-is-who (wait play samep)
- (unless samep
- (let ((wheel (gnugo-get :wheel)))
- (when wheel
- (gnugo--dance-dance
- (setcdr wheel (mapcar 'gnugo-other
- (cdr wheel)))))))
- (message "GNU Go %splays as %s, you as %s (%s)"
- (if samep "" "now ")
- wait play (if samep
- "as before"
- "NOTE: this is a switch!")))
-
-(defsubst gnugo--nodep (x)
- (keywordp (caar x)))
-
-(defun gnugo--SZ! (size)
- (gnugo-put :SZ size)
- (gnugo-put :center-position
- (funcall (gnugo--as-pos-func)
- (let ((c (+ -1 ?a (truncate (1+ size) 2))))
- (string c c)))))
-
-(defun gnugo--plant-and-climb (collection &optional sel)
- (gnugo-put :sgf-collection collection)
- (let ((tree (nth (or sel 0) collection)))
- (gnugo-put :sgf-gametree tree)
- (gnugo-put :monkey (vector
- ;; mem
- (aref (gnugo--tree-ends tree) 0)
- ;; bidx
- 0))
- tree))
-
-(defun gnugo-read-sgf-file (filename)
- "Load the first game tree from FILENAME, a file in SGF format."
- (interactive "fSGF file to load: ")
- (when (file-directory-p filename)
- (user-error "Cannot load a directory (try a filename with extension
.sgf)"))
- (let (play wait samep coll tree game-over)
- ;; problem: requiring GTP `loadsgf' complicates network subproc support;
- ;; todo: skip it altogether when confident about `gnugo/sgf-create'
- (setq play (gnugo--q/ue "loadsgf %s" (expand-file-name filename))
- wait (gnugo-other play)
- samep (string= (gnugo-get :user-color) play))
- (gnugo-put :last-mover wait)
- (unless samep
- (gnugo-put :gnugo-color wait)
- (gnugo-put :user-color play))
- (setq coll (gnugo/sgf-create filename)
- tree (gnugo--plant-and-climb
- coll (let ((n (length coll)))
- ;; This is better:
- ;; (if (= 1 n)
- ;; 0
- ;; (let* ((q (format "Which game? (1-%d)" n))
- ;; (choice (1- (read-number q 1))))
- ;; (if (and (< -1 choice) (< choice n))
- ;; choice
- ;; (message "(Selecting the first game)")
- ;; 0)))
- ;; but this is what we use (for now) to accomodate
- ;; (aka faithfully mimic) GTP `loadsgf' limitations:
- (unless (= 1 n)
- (message "(Selecting the first game)"))
- 0)))
- ;; This is deliberately undocumented for now.
- (gnugo--SZ! (gnugo--root-prop :SZ tree))
- (when (setq game-over (or (gnugo--root-prop :RE tree)
- (when (equal '("PASS" "PASS")
- (gnugo-move-history 'two))
- 'two-passes)))
- (gnugo-close-game nil game-over))
- (gnugo-put :last-user-bpos
- (gnugo-move-history 'bpos (gnugo-get :user-color)))
- (gnugo-refresh t)
- (gnugo--ok-file filename)
- (gnugo--who-is-who wait play samep)))
-
-(defun gnugo--mem-with-played-stone (pos &optional noerror)
- (let ((color (cl-case (following-char)
- (?X :B)
- (?O :W))))
- (if (not color)
- (unless noerror
- (user-error "No stone at %s" pos))
- (cl-loop
- with fruit = (cons color (funcall (gnugo--as-cc-func) pos))
- for mem on (aref (gnugo-get :monkey) 0)
- when (equal fruit (caar mem))
- return mem
- finally return nil))))
-
-(defun gnugo--climb-towards-root (spec &optional reaction keep)
- (gnugo-gate)
- (gnugo--assist-state t)
- (let* ((user-color (gnugo-get :user-color))
- (monkey (gnugo-get :monkey))
- (tree (gnugo-get :sgf-gametree))
- (ends (gnugo--tree-ends tree))
- (remorseful (not (gnugo--no-regrets monkey ends)))
- (stop (if (numberp spec)
- (nthcdr (if (zerop spec)
- (if (string= (gnugo-get :last-mover)
- user-color)
- 1
- 2)
- spec)
- (aref monkey 0))
- (cdr (gnugo--mem-with-played-stone
- (if (stringp spec)
- spec
- (gnugo-position)))))))
- (when (gnugo-get :game-over)
- (gnugo--unclose-game))
- (while (and (not (eq stop (aref monkey 0)))
- (gnugo--no-worries (gnugo--q "undo")))
- (pop (aref monkey 0))
- (gnugo-put :last-mover (gnugo-current-player))
- (gnugo--merge-showboard-results) ; all
- (gnugo-refresh) ; this
- (redisplay)) ; eye candy
- (let* ((ulastp (string= (gnugo-get :last-mover) user-color))
- (ubpos (gnugo-move-history (if ulastp 'car 'cadr))))
- (gnugo-put :last-user-bpos (if (and ubpos (not (gnugo--passp ubpos)))
- ubpos
- (gnugo-get :center-position)))
- (gnugo-refresh t)
- (unless (or keep remorseful)
- (aset ends (aref monkey 1) (aref monkey 0)))
- (when ulastp
- (let ((g (gnugo-get :gnugo-color)))
- (cl-flet
- ((turn () (gnugo--turn-the-wheel t)))
- (cl-case (or reaction gnugo-undo-reaction)
- (play (turn))
- (play! (let ((wheel (gnugo-get :wheel)))
- (cl-letf (((cdr wheel) (cons g (cdr wheel))))
- (turn))))
- (zombie (gnugo-zombie-mode 1))
- (t (gnugo-put :one-shot g)))))))))
-
-(defun gnugo-undo-one-move (&optional me-next)
- "Undo exactly one move (perhaps GNU Go's, perhaps yours).
-Do not schedule a move by GNU Go even if it is GNU Go's turn to play.
-Prefix arg ME-NEXT means to arrange for you to play
-the color of the next move (and GNU Go the opposite).
-This is useful after loading an SGF file whose last
-move was done by the color you prefer to play:
- \\[gnugo-read-sgf-file] FILENAME RET
- C-u \\[gnugo-undo-one-move]
-
-See also `gnugo-undo-two-moves'."
- (interactive "P")
- (gnugo-gate)
- (when me-next
- (let* ((play (gnugo-get :last-mover))
- (wait (gnugo-other play))
- (samep (string= play (gnugo-get :user-color))))
- (gnugo-put :user-color play)
- (gnugo-put :gnugo-color wait)
- (gnugo--who-is-who wait play samep)))
- (gnugo--climb-towards-root 1 (cl-case gnugo-undo-reaction
- (zombie gnugo-undo-reaction)
- (t 'one-shot))))
-
-(defun gnugo-undo-two-moves ()
- "Undo a pair of moves (GNU Go's and yours).
-However, if you are the last mover, undo only one move.
-Regardless, after undoing, it is your turn to play again."
- (interactive)
- (gnugo--climb-towards-root 0))
-
-(defun gnugo-oops (&optional position)
- "Like `gnugo-undo-two-moves', but keep the undone moves.
-The kept moves become a sub-gametree (variation) when play resumes.
-Prefix arg means, instead, undo repeatedly up to and including
-the move which placed the stone at point, like `\\[gnugo-fancy-undo]'."
- (interactive "P")
- (gnugo--climb-towards-root (unless position
- 0)
- nil t))
-
-(defun gnugo-okay (&optional full)
- "Redo a pair of undone moves.
-Prefix arg means to redo all the undone moves."
- (interactive "P")
- (gnugo-gate)
- (let* ((tree (gnugo-get :sgf-gametree))
- (ends (gnugo--tree-ends tree))
- (monkey (gnugo-get :monkey)))
- (if (gnugo--no-regrets monkey ends)
- (message "Oop ack!")
- (let* ((as-pos (gnugo--as-pos-func))
- (mnum (gnugo--tree-mnum tree))
- (mem (aref monkey 0))
- (bidx (aref monkey 1))
- (end (aref ends bidx))
- (ucolor (gnugo-get :user-color))
- (uprop (gnugo--prop<-color ucolor)))
- (cl-flet
- ((mvno (node) (gethash node mnum)))
- (cl-loop
- with ok = (if full
- (mvno (car end))
- (+ 2 (mvno (car mem))))
- with (node move todo)
- for ls on end
- do (progn
- (setq node (car ls)
- move (gnugo--move-prop node))
- (when (and move (>= ok (mvno node)))
- (let ((userp (eq uprop (car move))))
- (push (list userp
- (funcall as-pos (cdr move)))
- todo))))
- until (eq mem (cdr ls))
- finally do
- (cl-loop
- for (userp pos) in todo
- do (progn
- (gnugo-push-move userp pos)
- (gnugo-refresh)
- (redisplay)))))))))
-
-(defun gnugo-display-final-score (&optional comment)
- "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
-making sure two \"pass\" moves are played consecutively, if necessary).
-Also, add the `RE' (Result) SGF property to the root node of the game tree.
-Prefix arg means to attach to the last move the SGF properties:
- TB -- Black Territory
- TW -- White Territory
- MA -- Mark (to indicate seki stones)
- DD -- Dim Points (to indicate dead stones)
-each of which is a non-empty list of positions, as well as the final
-score text (slightly compacted) as a comment."
- (interactive "P")
- (let ((game-over (gnugo-get :game-over)))
- (unless (or game-over
- (and (not (gnugo-get :waiting))
- (y-or-n-p "Game still in play. Stop play now? ")))
- (user-error "Sorry, game still in play"))
- (unless game-over
- (cl-flet
- ((pass (userp)
- (message "Playing PASS for %s ..."
- (gnugo-get (if userp :user-color :gnugo-color)))
- (sit-for 1)
- (gnugo-push-move userp "PASS")))
- (unless (pass t)
- (pass nil)))
- (gnugo-refresh)
- (sit-for 3)))
- (let ((b= " Black = ")
- (w= " White = ")
- (res (when (gnugo--resignp (gnugo-move-history 'car))
- (gnugo-get :last-mover)))
- (seed (gnugo-get :scoring-seed))
- blurb result)
- (if res
- (setq blurb (list
- (format "%s wins.\n"
- (substring (if (= ?b (aref res 0)) w= b=)
- 3 8))
- "The game is over.\n"
- (format "Resignation by %s.\n" res))
- result (concat (upcase (substring (gnugo-other res) 0 1))
- "+Resign"))
- (message "Computing final score ...")
- (let* ((g-over (gnugo-get :game-over))
- (live (gnugo-aqr 'live g-over))
- (dead (gnugo-aqr 'dead g-over))
- (terr-q (format "final_status_list %%s_territory %d" seed))
- (terr "territory")
- (capt "captures")
- (b-terr (gnugo--count-query terr-q "black"))
- (w-terr (gnugo--count-query terr-q "white"))
- (b-capt (string-to-number (gnugo-get :black-captures)))
- (w-capt (string-to-number (gnugo-get :white-captures)))
- (komi (gnugo--root-prop :KM)))
- (setq blurb (list "The game is over. Final score:\n")
- result (gnugo-query "final_score %d" seed))
- (cond ((string= "Chinese" (gnugo--root-prop :RU))
- (dolist (group live)
- (cl-incf (if (gnugo--prop-blackp (caar group))
- b-terr
- w-terr)
- (length (cdr group))))
- (dolist (group dead)
- (cl-incf (if (gnugo--prop-blackp (caar group))
- w-terr
- b-terr)
- (length (cdr group))))
- (push (format "%s%d %s = %3.1f\n" b= b-terr terr b-terr) blurb)
- (push (format "%s%d %s + %3.1f %s = %3.1f\n" w=
- w-terr terr komi 'komi (+ w-terr komi))
- blurb))
- (t
- (dolist (group dead)
- (cl-incf (if (gnugo--prop-blackp (caar group))
- w-terr
- b-terr)
- (* 2 (length (cdr group)))))
- (push (format "%s%d %s + %s %s = %3.1f\n" b=
- b-terr terr
- b-capt capt
- (+ b-terr b-capt))
- blurb)
- (push (format "%s%d %s + %s %s + %3.1f %s = %3.1f\n" w=
- w-terr terr
- w-capt capt
- komi 'komi
- (+ w-terr w-capt komi))
- blurb)))
- (push (if (string= "0" result)
- "The game is a draw.\n"
- (format "%s wins by %s.\n"
- (substring (if (= ?B (aref result 0)) b= w=) 3 8)
- (substring result 2)))
- blurb)
- (message "Computing final score ... done")))
- ;; extra info
- (let ((beg (gnugo-get :game-start-time))
- (end (gnugo-get :game-end-time)))
- (when end
- (push "\n" blurb)
- (cl-flet
- ((yep (pretty moment)
- (push (format-time-string
- (concat pretty ": %F %T %z\n")
- moment)
- blurb)))
- (yep "Game start" beg)
- (yep " end" end))))
- (setq blurb (apply 'concat (nreverse blurb)))
- (gnugo--set-root-prop :RE result)
- (when comment
- (let ((node (car (aref (gnugo-get :monkey) 0))))
- (cl-loop
- for (prop . what) in '((:TB . black_territory)
- (:TW . white_territory)
- (:MA . seki)
- (:DD . dead))
- do (let ((ls (gnugo-lsquery "final_status_list %s %s"
- what seed)))
- (delq (assq prop node) node)
- (when ls
- (gnugo--decorate
- node prop (mapcar (gnugo--as-cc-func)
- ls)))))
- (gnugo--decorate
- (delq (assq :C node) node)
- :C
- (with-temp-buffer ; lame
- (insert blurb)
- (when (search-backward "\n\nGame start:" nil t)
- (delete-region (point) (point-max)))
- (cl-flet
- ((rep (old new)
- (goto-char (point-min))
- (while (search-forward old nil t)
- (replace-match new))))
- (rep "The game is over. " "")
- (rep "territory" "T")
- (rep "captures" "C")
- (rep "komi" "K"))
- (buffer-string)))))
- (switch-to-buffer (format "%s*GNUGO Final Score*" (gnugo-get :diamond)))
- (erase-buffer)
- (insert blurb)))
-
-(defun gnugo-quit ()
- "Kill the current buffer, assumed to be in GNUGO Board mode, maybe.
-If the game is not over, ask for confirmation first."
- (interactive)
- (if (or (gnugo-get :game-over)
- (y-or-n-p "Quit? "))
- (kill-buffer nil)
- (message "(not quitting)")))
-
-(defun gnugo-leave-me-alone ()
- "Kill the current buffer unconditionally."
- (interactive)
- (kill-buffer nil))
-
-(defun gnugo-fancy-undo (count)
- "Rewind the game tree in various ways.
-Prefix arg COUNT means to undo that many moves.
-Otherwise, undo repeatedly up to and including the move
-which placed the stone at point."
- (interactive "P")
- (gnugo--climb-towards-root
- (if (numberp count)
- count
- (car-safe count))))
-
-(define-minor-mode gnugo-image-display-mode
- "If enabled, display the board using images.
-See function `display-images-p' and variable `gnugo-xpms'."
- :variable
- ((gnugo-get :display-using-images)
- .
- (lambda (bool)
- (unless (eq bool (gnugo-get :display-using-images))
- (unless (display-images-p)
- (user-error "Sorry, display does not support images"))
- (let ((fresh (if (functionp gnugo-xpms)
- (funcall gnugo-xpms (gnugo-get :SZ))
- gnugo-xpms)))
- (unless fresh
- (user-error "Sorry, `gnugo-xpms' unset"))
- (unless (eq fresh (gnugo-get :xpms))
- (gnugo-put :xpms fresh)
- (gnugo--forget :all-yy)))
- (mapc (let ((act (if bool
- 'display
- 'do-not-display)))
- (lambda (yy)
- (setcar (symbol-plist yy) act)))
- (or (gnugo-get :all-yy)
- (gnugo-put :all-yy
- (prog1 (mapcar (lambda (ent)
- (let* ((k (car ent))
- (yy (gnugo-yy (cdr k) (car k))))
- (setplist yy `(not-yet ,(cdr ent)))
- yy))
- (gnugo-get :xpms))
- (gnugo-put :imul
- (image-size (get (gnugo-yy 5 (gnugo-yang ?+))
- 'not-yet)))))))
- (setplist (gnugo-f 'ispc) (and bool '(display (space :width 0))))
- (gnugo-put :highlight-last-move-spec
- (if bool
- `(,(lambda (p)
- (get (gnugo-yy (get-text-property p 'gnugo-yin)
- (get-text-property p 'gnugo-yang)
- t)
- 'display))
- 0 delete-overlay)
- (gnugo-get :default-highlight-last-move-spec)))
- (gnugo-put :mul (if bool
- (gnugo-get :imul)
- '(1 . 1)))
- (gnugo-put :display-using-images bool)
- (save-excursion (gnugo-refresh t))))))
-
-(defsubst gnugo--node-with-played-stone (pos &optional noerror)
- (car (gnugo--mem-with-played-stone pos noerror)))
-
-(defun gnugo-describe-position ()
- "Display the board position under cursor in the echo area.
-If there a stone at that position, also display its move number."
- (interactive)
- (let* ((pos (gnugo-position)) ; do first (can throw)
- (node (gnugo--node-with-played-stone pos t)))
- (message
- "%s%s" pos
- (or (when node
- (let* ((tree (gnugo-get :sgf-gametree))
- (mnum (gnugo--tree-mnum tree))
- (move-num (gethash node mnum)))
- (format " (move %d)" move-num)))
- ""))))
-
-(defun gnugo-switch-to-another ()
- "Switch to another GNU Go game buffer (if any)."
- (interactive)
- (cl-loop
- for buf in (cdr (buffer-list))
- if (gnugo-board-buffer-p buf)
- return (progn
- (bury-buffer)
- (switch-to-buffer buf))
- finally do (message "(only one)")))
-
-(defun gnugo-comment (node comment)
- "Add to NODE a COMMENT (string) property.
-Interactively, NODE is the one corresponding to the stone at point,
-or the root node if there is no played stone at point, and any
-previous comment is inserted as the initial-input (see `read-string').
-
-If COMMENT is nil or the empty string, remove the property entirely."
- (interactive
- (let* ((pos (gnugo-position))
- (node (or (gnugo--node-with-played-stone pos t)
- (gnugo--root-node))))
- (list node
- (read-string (format "Comment for %s: "
- (if (eq node (gnugo--root-node))
- "root node"
- (gnugo-describe-position)))
- (gnugo-aqr :C node)))))
- (setq node (delq (assq :C node) node))
- (unless (zerop (length comment))
- (gnugo--decorate node :C comment)))
-
-(defun gnugo--struggle (prop updn)
- (unless (eq updn (gnugo--:karma prop)) ; drudgery avoidance
- (let ((color (gnugo-get prop)))
- (if updn
- ;; enable
- (gnugo-gate)
- ;; disable
- (let ((waiting (gnugo-get :waiting)))
- (when (and waiting (string= color (car waiting)))
- (gnugo--rename-buffer-portion)
- (setcdr waiting
- ;; heuristic: Warp only if it appears
- ;; that the user is "following along".
- (or (ignore-errors
- (string= (gnugo-position)
- (gnugo-move-history 'bpos color)))
- 'nowarp))
- (gnugo--display-suggestion color "forthcoming")
- (sit-for 2))))
- (let* ((wheel (gnugo-get :wheel))
- (timer (car wheel))
- (karma (cdr wheel)))
- (when (timerp timer)
- (cancel-timer timer))
- (setcar wheel nil)
- (setcdr wheel (setq karma
- ;; walk to the west, fly to the east,
- ;; talk and then rest, cry and then feast.
- ;; 99 beers down thirsty throats sloshed?
- ;; 500 years 'neath pink mountains squashed?
- ;; balk with the best, child now re-creased!
- (if updn
- (push color karma)
- (delete color karma))))
- (gnugo--dance-dance karma))
- (gnugo--turn-the-wheel t))))
-
-(define-minor-mode gnugo-assist-mode
- "If enabled (\"Assist\" in mode line), GNU Go plays for you.
-When disabling, if GNU Go has already started thinking of
-a move to play for you, the thinking is not cancelled but instead
-transformed into a move suggestion (see `gnugo-request-suggestion')."
- :variable
- ((gnugo--assist-state)
- .
- (lambda (bool)
- (gnugo--struggle :user-color bool))))
-
-(define-minor-mode gnugo-zombie-mode
- "If enabled (\"Zombie\" in mode line), GNU Go lets you play for it.
-When disabling, if GNU Go has already started thinking of
-a move to play, the thinking is not cancelled but instead
-transformed into a move suggestion (see `gnugo-request-suggestion')."
- :variable
- ((not (gnugo--:karma :gnugo-color))
- .
- (lambda (bool)
- (gnugo--struggle :gnugo-color (not bool)))))
-
-;;;---------------------------------------------------------------------------
-;;; Command properties and gnugo-command
-
-;; GTP commands entered by the user are never issued directly to GNU Go;
-;; instead, their behavior and output are controlled by the property
-;; `:gnugo-gtp-command-spec' hung off of each (interned/symbolic) command.
-;; The value of this property is a sub-plist, w/ sub-properties as follows:
-;;
-;; :full -- completely interpret the command string; the value is a
-;; func that takes the list of words derived from splitting the
-;; command string (minus the command) and handles everything.
-;;
-;; :output -- either a keyword specifying the preferred output method:
-;; :message -- show output in minibuffer
-;; :discard -- sometimes you just don't care;
-;; or a function that takes one arg, the output string, and
-;; handles it completely. default is to switch to buffer
-;; "*gnugo command output*" if the output has a newline,
-;; otherwise use `message'.
-;;
-;; :post-thunk -- run after output processing (at the very end).
-
-(defun gnugo-command (command)
- "Send the Go Text Protocol COMMAND (a string) to GNU Go.
-Output and Emacs behavior depend on which command is given (some
-commands are handled completely by Emacs w/o using the subprocess;
-some commands have their output displayed in specially prepared
-buffers or in the echo area; some commands are instrumented to do
-gnugo.el-specific housekeeping).
-
-For example, for the command \"help\", Emacs visits the
-GTP command reference info page.
-
-NOTE: At this time, GTP command handling specification is still
- incomplete. Thus, some commands WILL confuse gnugo.el."
- (interactive "sCommand: ")
- (if (string= "" command)
- (message "(no command given)")
- (let* ((split (split-string command))
- (cmd (intern (car split)))
- (spec (get cmd :gnugo-gtp-command-spec))
- (full (plist-get spec :full)))
- (if full
- (funcall full (cdr split))
- (message "Doing %s ..." command)
- (let* ((ans (gnugo--q command))
- (where (plist-get spec :output)))
- (if (string-match "unknown.command" ans)
- (message "%s" ans)
- (cond ((functionp where) (funcall where ans))
- ((eq :discard where) (message ""))
- ((or (eq :message where)
- (not (string-match "\n" ans)))
- (message "%s" ans))
- (t (switch-to-buffer "*gnugo command output*")
- (erase-buffer)
- (insert ans)
- (message "Doing %s ... done" command)))
- (let ((thunk (plist-get spec :post-thunk)))
- (when thunk (funcall thunk)))))))))
-
-;;;---------------------------------------------------------------------------
-;;; Major mode for interacting with a GNUGO subprocess
-
-(define-derived-mode gnugo-board-mode special-mode "GNUGO Board"
- "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 (see `gnugo-board-mode-map')."
- ;; hint: (search-forward "define-key gnugo-board-mode-map")
- :syntax-table nil
- :abbrev-table nil
- (buffer-disable-undo) ; todo: undo undo undoing
- (setq font-lock-defaults '(gnugo-font-lock-keywords t)
- truncate-lines t)
- (add-hook 'kill-buffer-hook 'gnugo-cleanup nil t)
-
- ;; Previously, we used ‘(eq gnugo--intangible 'cursor-intangible)’
- ;; here, but (a) this is a bit quicker; and (b) we want to placate
- ;; Emacs 24, which blurts out a "not known to be defined" warning.
- ;; (Apparently it cannot infer equivalence of the two conditions.)
- (if (fboundp 'cursor-intangible-mode)
- (cursor-intangible-mode 1)
- ;; Make sure ‘intangible’ DTRT in this buffer.
- (setq-local inhibit-point-motion-hooks nil))
-
- (setq-local gnugo-state (gnugo--mkht :size (1- 42)))
- (setq-local gnugo-btw nil)
- (add-to-list 'minor-mode-alist '(gnugo-btw gnugo-btw))
- (gnugo-put :highlight-last-move-spec
- (gnugo-put :default-highlight-last-move-spec '("(" -1 nil)))
- (gnugo-put :paren-ov (cons (make-overlay 1 1)
- (let ((ov (make-overlay 1 1)))
- (overlay-put ov 'display ")")
- ov)))
- (gnugo-put :mul '(1 . 1))
- (gnugo-put :obarray (make-vector 31 nil))
- (add-to-invisibility-spec :nogrid))
-
-;;;---------------------------------------------------------------------------
-;;; Entry point
-
-;;;###autoload
-(defun gnugo (&optional new-game)
- "Run gnugo in a buffer, or resume a game in progress.
-If there is already a game in progress you may resume it instead
-of starting a new one. Prefix arg means skip the game-in-progress
-check and start a new game straight away.
-
-Before starting, Emacs queries you for additional command-line
-options (Emacs supplies \"--mode gtp --quiet\" automatically).
-
-Note that specifying \"--infile FILENAME\" (or, \"-l FILENAME\")
-silently clobbers certain other options, such as \"--color\".
-For details, see info node `(gnugo) Invoking GNU Go'.
-
-\\<gnugo-board-mode-map>
-To play, use \\[gnugo-move] to place a stone or \\[gnugo-pass] to pass.
-See `gnugo-board-mode' for a full list of commands."
- (interactive "P")
- (let* ((all (let (acc)
- (dolist (buf (buffer-list))
- (when (gnugo-board-buffer-p buf)
- (push (cons (buffer-name buf) buf) acc)))
- acc))
- (n (length all)))
- (if (and (not new-game)
- (cl-plusp n)
- (y-or-n-p (format "GNU Go game%s in progress, resume play? "
- (if (= 1 n) "" "s"))))
- ;; resume
- (switch-to-buffer
- (cdr (if (= 1 n)
- (car all)
- (let ((sel (completing-read "Which one? " all nil t)))
- (if (string= "" sel)
- (car all)
- (assoc sel all))))))
- ;; sanity check
- (unless (executable-find gnugo-program)
- (user-error "Invalid `gnugo-program': %S" gnugo-program))
- ;; set up a new board
- (switch-to-buffer (generate-new-buffer "(Uninitialized GNUGO Board)"))
- (gnugo-board-mode)
- (let* ((filename nil)
- (user-color "black")
- (args (cl-loop
- with ls = (split-string
- ;; todo: grok ‘gnugo --help’; completion
- (read-string
- "GNU Go options: "
- (car gnugo-option-history)
- 'gnugo-option-history))
- with ok
- while ls do
- (let ((arg (pop ls)))
- (cl-flet
- ((ex (opt fn)
- (if filename
- (warn "%s %s ignored" opt fn)
- (setq filename fn))))
- (cond
- ((string= "--color" arg)
- (push arg ok)
- (push
- ;; Unfortunately, GTP does not provide
- ;; a way to query the user color, so
- ;; we must resort to this weirdness.
- (setq user-color
- (pop ls))
- ok))
- ((string= "--infile" arg)
- (ex "--infile" (pop ls)))
- ((string-match "^-l" arg)
- (ex "-l" (if (< 2 (length arg))
- (substring arg 2)
- (pop ls))))
- (t (push arg ok)))))
- finally return (nreverse ok)))
- (proc (apply 'start-process "gnugo"
- (current-buffer)
- gnugo-program
- "--mode" "gtp" "--quiet"
- args))
- root board-size handicap komi)
- (gnugo-put :user-color user-color)
- (gnugo-put :proc proc)
- (set-process-sentinel proc 'gnugo-sentinel)
- ;; Emacs is too protective sometimes, blech.
- (set-process-query-on-exit-flag proc nil)
- (gnugo-put :diamond (substring (process-name proc) 5))
- (gnugo-put :gnugo-color (gnugo-other user-color))
- (if filename
- (gnugo-read-sgf-file (expand-file-name filename))
- (cl-flet
- ((r! (&rest plist) (apply 'gnugo--decorate root plist)))
- (gnugo--SZ!
- (setq root (gnugo--root-node
- (gnugo--plant-and-climb
- (gnugo/sgf-create "(;FF[4]GM[1])" t)))
- komi (gnugo--nquery "get_komi")
- handicap (gnugo--nquery "get_handicap")
- board-size (gnugo--nquery "query_boardsize")))
- ;; Work around a GNU Go 3.8 (and possibly earlier/later)
- ;; bug whereby GTP command ‘get_handicap’ fails to return
- ;; the N set by ‘--handicap N’ on the command line.
- (let ((actually (member "--handicap" args)))
- ;; Checking ‘(zerop handicap)’ first is not strictly
- ;; necessary; it represents a hope that some day GNU Go
- ;; will DTRT (or provide rationale for this weird behavior)
- ;; and become worthy of our trust.
- (when (and (zerop handicap) actually)
- (setq handicap (string-to-number (cadr actually)))))
- (r! :SZ board-size
- :DT (format-time-string "%F")
- :RU (if (member "--chinese-rules" args)
- "Chinese"
- "Japanese")
- :KM komi)
- (let ((ub (gnugo--blackp user-color)))
- (r! (if ub :PW :PB) (concat "GNU Go " (gnugo-query "version"))
- (if ub :PB :PW) (user-full-name)))
- (unless (zerop handicap)
- (r! :HA handicap
- :AB (mapcar (gnugo--as-cc-func)
- (gnugo-lsquery "fixed_handicap %d"
- handicap)))))))
- (gnugo-put :waiting-start (current-time))
- (gnugo-refresh t)
- (gnugo-goto-pos (or (gnugo-get :last-user-bpos)
- (gnugo-get :center-position)))
- ;; first move
- (gnugo-put :game-start-time (current-time))
- (let ((g (gnugo-get :gnugo-color))
- (n (or (gnugo--root-prop :HA) 0))
- (u (gnugo-get :user-color)))
- (unless (gnugo-get :last-mover)
- (gnugo-put :last-mover
- (if (or (and (gnugo--blackp u) (< 1 n))
- (and (gnugo--blackp g) (< n 2)))
- u
- g)))
- (let ((karma (list g)))
- (gnugo-put :wheel (cons nil karma))
- (gnugo--dance-dance karma))
- (run-hooks 'gnugo-start-game-hook)
- (gnugo--turn-the-wheel)))))
-
-;;;---------------------------------------------------------------------------
-;;; Load-time actions
-
-(unless (get 'help :gnugo-gtp-command-spec)
- (cl-flet*
- ((sget (x) (get x :gnugo-gtp-command-spec))
- (jam (cmd prop val) (put cmd :gnugo-gtp-command-spec
- (plist-put (sget cmd) prop val)))
- (validpos (s &optional go)
- (let ((pos (upcase s)))
- (cl-loop
- with size = (gnugo-get :SZ)
- for c across (funcall (gnugo--as-cc-func)
- pos)
- do (let ((norm (- c ?a)))
- (unless (and (< -1 norm)
- (> size norm))
- (user-error "Invalid position: %s"
- pos))))
- (when go
- (gnugo-goto-pos pos))
- pos))
- (defgtp (x &rest props) (dolist (cmd (if (symbolp x) (list x) x))
- (let ((ls props))
- (while ls
- (jam cmd (car ls) (cadr ls))
- (setq ls (cddr ls)))))))
- (cl-macrolet ((deffull (who &body body)
- (declare (indent 1))
- `(defgtp ',who :full (lambda (sel)
- ,@body))))
-
- (deffull help
- (info "(gnugo)GTP command reference")
- (when sel (setq sel (intern (car sel))))
- (let (buffer-read-only pad cur spec output found)
- (cl-flet
- ((note (s) (insert pad "[NOTE: gnugo.el " s ".]\n")))
- (goto-char (point-min))
- (save-excursion
- (while (re-search-forward "^ *[*] \\([a-zA-Z_]+\\)\\(:.*\\)*\n"
- nil t)
- (unless pad
- (setq pad (make-string (- (match-beginning 1)
- (match-beginning 0))
- 32)))
- (when (plist-get
- (setq spec
- (get (setq cur (intern (match-string 1)))
- :gnugo-gtp-command-spec))
- :full)
- (note "handles this command completely"))
- (when (setq output (plist-get spec :output))
- (if (functionp output)
- (note "handles the output specially")
- (cl-case output
- (:discard (note "discards the output"))
- (:message (note "displays the output in the echo
area")))))
- (when (eq sel cur)
- (setq found (make-marker))
- (set-marker found (match-beginning 0))))))
- (cond (found (goto-char found) (set-marker found nil))
- ((not sel))
- (t (message "(no such command: %s)" sel)))))
-
- (deffull final_score
- ;; Explicit ignorance avoids byte-compiler warning.
- (ignore sel)
- (gnugo-display-final-score))
-
- (defgtp '(boardsize
- clear_board
- fixed_handicap)
- :output :discard
- :post-thunk (lambda ()
- (gnugo--unclose-game)
- (gnugo--forget :last-mover)
- ;; ugh
- (gnugo--SZ! (gnugo--nquery "query_boardsize"))
- (gnugo-refresh t)))
-
- (deffull loadsgf
- (gnugo-read-sgf-file (car sel)))
-
- (deffull (undo gg-undo)
- (gnugo--climb-towards-root
- (let (n)
- (cond ((not sel) 1)
- ((cl-plusp (setq n (string-to-number (car sel)))) n)
- (t (validpos (car sel) t)))))))))
-
-;; Respect user customizations; try not to clobber the keymap on reload.
-;; This (top-level, conditionalized, late-in-file) form is not "idiomatic",
-;; but it does avoid forward references [which are ugly, IMHO --ttn].
-;; TODO: Link to emacs-devel thread, here.
-;;
-;; hacker override: (define-key gnugo-board-mode-map "?" nil)
-(unless (eq 'describe-mode (lookup-key gnugo-board-mode-map "?"))
- (cl-loop
- for (key binding . _)
- on '("?" describe-mode
- "S" gnugo-request-suggestion
- "\C-m" gnugo-move
- " " gnugo-move
- "P" gnugo-pass
- "R" gnugo-resign
- "q" gnugo-quit
- "Q" gnugo-leave-me-alone
- "U" gnugo-fancy-undo
- "\M-u" gnugo-undo-one-move
- "u" gnugo-undo-two-moves
- "\C-?" gnugo-undo-two-moves
- "o" gnugo-oops
- "O" gnugo-okay
- "\C-l" gnugo-refresh
- "\M-_" gnugo-boss-is-near
- "_" gnugo-boss-is-near
- "h" gnugo-move-history
- "L" gnugo-frolic-in-the-leaves
- "\C-c\C-l" gnugo-frolic-in-the-leaves
- "i" gnugo-image-display-mode
- "w" gnugo-worm-stones
- "W" gnugo-worm-data
- "d" gnugo-dragon-stones
- "D" gnugo-dragon-data
- "g" gnugo-grid-mode
- "!" gnugo-estimate-score
- ":" gnugo-command
- ";" gnugo-command
- "=" gnugo-describe-position
- "s" gnugo-write-sgf-file
- "\C-x\C-s" gnugo-write-sgf-file
- "\C-x\C-w" gnugo-write-sgf-file
- "l" gnugo-read-sgf-file
- "F" gnugo-display-final-score
- "A" gnugo-switch-to-another
- "C" gnugo-comment
- "\C-c\C-a" gnugo-assist-mode
- "\C-c\C-z" gnugo-zombie-mode
- ;; mouse
- [(down-mouse-1)] gnugo-mouse-move
- [(down-mouse-2)] gnugo-mouse-move ; mitigate accidents
- [(down-mouse-3)] gnugo-mouse-pass
- ;; delving into the curiosities
- "\C-c\C-p" gnugo-describe-internal-properties)
- by #'cddr
- do (define-key gnugo-board-mode-map key binding)))
-
-(provide 'gnugo)
-
-
-;;;---------------------------------------------------------------------------
-;;; The remainder of this file defines a simplified SGF-handling library.
-;;; When/if it should start to attain generality, it should be split off into
-;;; a separate file (probably named sgf.el) w/ funcs and vars renamed sans the
-;;; "gnugo/" prefix.
-
-(defconst gnugo/sgf-*r4-properties*
- '((AB "Add Black" setup list stone)
- (AE "Add Empty" game list point)
- (AN "Annotation" game simpletext)
- (AP "Application" root (simpletext . simpletext))
- (AR "Arrow" - list (point . point))
- (AS "Who adds stones" - simpletext) ; (LOA)
- (AW "Add White" setup list stone)
- (B "Black" move move)
- (BL "Black time left" move real)
- (BM "Bad move" move double)
- (BR "Black rank" game simpletext)
- (BT "Black team" game simpletext)
- (C "Comment" - text)
- (CA "Charset" root simpletext)
- (CP "Copyright" game simpletext)
- (CR "Circle" - list point)
- (DD "Dim points" - elist point) ; (inherit)
- (DM "Even position" - double)
- (DO "Doubtful" move none)
- (DT "Date" game simpletext)
- (EV "Event" game simpletext)
- (FF "Fileformat" root [number (1 . 4)])
- (FG "Figure" - (or none (number . simpletext)))
- (GB "Good for Black" - double)
- (GC "Game comment" game text)
- (GM "Game" root [number (1 . 20)])
- (GN "Game name" game simpletext)
- (GW "Good for White" - double)
- (HA "Handicap" game number) ; (Go)
- (HO "Hotspot" - double)
- (IP "Initial pos." game simpletext) ; (LOA)
- (IT "Interesting" move none)
- (IY "Invert Y-axis" game simpletext) ; (LOA)
- (KM "Komi" game real) ; (Go)
- (KO "Ko" move none)
- (LB "Label" - list (point . simpletext))
- (LN "Line" - list (point . point))
- (MA "Mark" - list point)
- (MN "set move number" move number)
- (N "Nodename" - simpletext)
- (OB "OtStones Black" move number)
- (ON "Opening" game text)
- (OT "Overtime" game simpletext)
- (OW "OtStones White" move number)
- (PB "Player Black" game simpletext)
- (PC "Place" game simpletext)
- (PL "Player to play" setup color)
- (PM "Print move mode" - number) ; (inherit)
- (PW "Player White" game simpletext)
- (RE "Result" game simpletext)
- (RO "Round" game simpletext)
- (RU "Rules" game simpletext)
- (SE "Markup" - point) ; (LOA)
- (SL "Selected" - list point)
- (SO "Source" game simpletext)
- (SQ "Square" - list point)
- (ST "Style" root [number (0 . 3)])
- (SU "Setup type" game simpletext) ; (LOA)
- (SZ "Size" root (or number (number . number)))
- (TB "Territory Black" - elist point) ; (Go)
- (TE "Tesuji" move double)
- (TM "Timelimit" game real)
- (TR "Triangle" - list point)
- (TW "Territory White" - elist point) ; (Go)
- (UC "Unclear pos" - double)
- (US "User" game simpletext)
- (V "Value" - real)
- (VW "View" - elist point) ; (inherit)
- (W "White" move move)
- (WL "White time left" move real)
- (WR "White rank" game simpletext)
- (WT "White team" game simpletext)
- (LT "Lose on time" setup simpletext))
- ;; r4-specific notes
- ;; - changed: DT FG LB RE RU SZ
- ;; - added: AP AR AS DD IP IY LN OT PM SE SQ ST SU VW
- "List of SGF[4] properties, each of the form (PROP NAME CONTEXT SPEC...).")
-
-(defun gnugo/sgf-create (file-or-data &optional data-p)
- "Return the SGF[4] collection parsed from FILE-OR-DATA.
-FILE-OR-DATA is a file name or SGF[4] data.
-Optional arg DATA-P non-nil means FILE-OR-DATA is
-a string containing SGF[4] data.
-A collection is a list of gametrees, each a vector of four elements:
-
- ENDS -- a vector of node lists, with shared tails
- (last element of all the lists is the root node)
-
- MNUM -- `eq' hash: node to move numbers; non-\"move\" nodes
- have a move number of the previous \"move\" node (or zero)
-
- ROOT -- the root node"
- ;; Arg names inspired by `create-image', despite -P being frowned upon.
- (let ((keywords (or (get 'gnugo/sgf-*r4-properties* :keywords)
- (put 'gnugo/sgf-*r4-properties* :keywords
- (mapcar (lambda (full)
- (cons (car full)
- (intern (format ":%s" (car full)))))
- gnugo/sgf-*r4-properties*))))
- (specs (or (get 'gnugo/sgf-*r4-properties* :specs)
- (put 'gnugo/sgf-*r4-properties* :specs
- (mapcar (lambda (full)
- (cons (car full) (cl-cdddr full)))
- gnugo/sgf-*r4-properties*))))
- SZ)
- (cl-labels
- ((sw () (skip-chars-forward " \t\n"))
- (x (end preserve-whitespace)
- (let ((beg (point))
- (endp (cl-case end
- (:end (lambda (char) (= ?\] char)))
- (:mid (lambda (char) (= ?\: char)))
- (t (lambda (char) (or (= ?\: char)
- (= ?\] char))))))
- c)
- (while (not (funcall endp (setq c (following-char))))
- (cond ((= ?\\ c)
- (delete-char 1)
- (if (eolp)
- (kill-line 1)
- (forward-char 1)))
- ((unless preserve-whitespace
- (looking-at "\\s-+"))
- (delete-region (point) (match-end 0))
- (insert " "))
- (t (forward-char 1))))
- (buffer-substring-no-properties beg (point))))
- (one (type end) (let ((s (progn
- (forward-char 1)
- (x end (eq 'text type)))))
- (cl-case type
- ((stone point move)
- ;; blech, begone bu"tt"-ugly blatherings
- ;; (but bide brobdingnagian boards)...
- (if (and (string= "tt" s)
- SZ
- (>= 19 SZ))
- ""
- s))
- ((simpletext color) s)
- ((number real double) (string-to-number s))
- ((text) s)
- ((none) "")
- (t (error "Unhandled type: %S" type)))))
- (val (spec) (cond ((symbolp spec)
- (one spec :end))
- ((vectorp spec)
- ;; todo: check range here.
- (one (aref spec 0) :end))
- ((eq 'or (car spec))
- (let ((v (one (cadr spec) t)))
- (if (= ?\] (following-char))
- v
- (forward-char 1)
- ;; todo: this assumes `spec' has the form
- ;; (or foo (foo . bar))
- ;; i.e., foo is not rescanned. e.g., `SZ'.
- ;; probably this assumption is consistent
- ;; w/ the SGF authors' desire to make the
- ;; parsing easy, but you never know...
- (cons v (one (cl-cdaddr spec) :end)))))
- (t (cons (one (car spec) :mid)
- (one (cdr spec) :end)))))
- (short (who) (when (eobp)
- (error "Unexpected EOF while reading %s" who)))
- (atvalp () (= ?\[ (following-char)))
- (PROP () (let (name spec ltype)
- (sw) (short 'property)
- (when (looking-at "[A-Z]")
- (setq name (read (current-buffer))
- spec (gnugo-aqr name specs))
- (sw)
- (cons
- (gnugo-aqr name keywords)
- (prog1 (if (= 1 (length spec))
- (val (car spec))
- (unless (memq (setq ltype (car spec))
- '(elist list))
- (error "Bad spec: %S" spec))
- (if (and (eq 'elist ltype) (sw)
- (not (atvalp)))
- nil
- (let ((type (cadr spec))
- mo ls)
- (while (and (sw) (atvalp)
- (setq mo (val type)))
- (push mo ls)
- (forward-char 1))
- (forward-char -1)
- (nreverse ls))))
- (forward-char 1))))))
- (morep () (and (sw) (not (eobp))))
- (seek (c) (and (morep) (= c (following-char))))
- (seek-into (c) (when (seek c)
- (forward-char 1)
- t))
- (NODE () (when (seek-into ?\;)
- (cl-loop
- with prop
- while (setq prop (PROP))
- collect (progn
- (when (eq :SZ (car prop))
- (setq SZ (cdr prop)))
- prop))))
- (TREE (parent mnum)
- (let ((ls parent)
- prev node)
- (seek-into ?\()
- (while (seek ?\;)
- (setq prev (car ls)
- node (NODE))
- (puthash node (+ (if (gnugo--move-prop node)
- 1
- 0)
- (gethash prev mnum 0))
- mnum)
- (push node
- ls))
- (prog1
- (if (not (seek ?\())
- ;; singular
- (list ls)
- ;; multiple
- (cl-loop
- while (seek ?\()
- append (TREE ls mnum)))
- (seek-into ?\))))))
- (with-temp-buffer
- (if (not data-p)
- (insert-file-contents file-or-data)
- (insert file-or-data)
- (goto-char (point-min)))
- (cl-loop
- while (morep)
- collect (let* ((mnum (gnugo--mkht :weakness 'key))
- (ends (TREE nil mnum))
- (root (car (last (car ends)))))
- (vector (apply 'vector ends)
- mnum
- root)))))))
-
-(defun gnugo/sgf-write-file (collection filename)
- (let ((aft-newline-appreciated '(:AP :GN :PB :PW :HA :KM :RU :RE))
- (specs (mapcar (lambda (full)
- (cons (intern (format ":%s" (car full)))
- (cl-cdddr full)))
- gnugo/sgf-*r4-properties*))
- p name v spec)
- (cl-labels
- ((esc (composed val)
- (mapconcat (lambda (c)
- (cl-case c
- ;; ‘?\[’ is not strictly required
- ;; but neither is it forbidden.
- ((?\[ ?\] ?\\) (format "\\%c" c))
- (?: (concat (if composed "\\" "") ":"))
- (t (string c))))
- ;; ‘string-to-list’ unnecessary; ‘mapconcat’ DTRT
- (if (stringp val)
- val
- (format "%s" val))
- ""))
- (>>one (v) (insert "[" (esc nil v)
- "]"))
- (>>two (v) (insert "[" (esc t (car v))
- ":" (esc t (cdr v))
- "]"))
- (>>nl () (cond ((memq name aft-newline-appreciated)
- (insert "\n"))
- ((< 60 (current-column))
- (save-excursion
- (goto-char p)
- (insert "\n")))))
- (>>prop (prop)
- (setq p (point)
- name (car prop)
- v (cdr prop))
- (insert (substring (symbol-name name) 1))
- (cond ((not v))
- ((and (consp v)
- (setq spec (gnugo-aqr name specs))
- (memq (car spec)
- '(list elist)))
- (>>nl)
- (let ((>> (if (consp (cadr spec))
- #'>>two
- #'>>one)))
- (dolist (little-v v)
- (setq p (point))
- (funcall >> little-v)
- (>>nl))))
- ((consp v)
- (>>two v) (>>nl))
- (t
- (>>one v) (>>nl))))
- (>>node (node)
- (cl-loop
- initially (insert ";")
- for prop in node
- do (>>prop prop)))
- (>>tree (tree)
- (unless (zerop (current-column))
- (newline))
- (insert "(")
- (dolist (x tree)
- (funcall (if (gnugo--nodep x)
- #'>>node
- #'>>tree)
- x))
- (insert ")")))
- (with-temp-buffer
- (dolist (tree collection)
- ;; write it out
- (let ((ht (gnugo--mkht))
- (leaves (append (gnugo--tree-ends tree) nil)))
- (cl-flet
- ((hang (stack)
- (cl-loop
- with rh ; rectified history
- with bp ; branch point
- for node in stack
- until (setq bp (gethash node ht))
- do (puthash node
- (push node rh) ; good for now: ½τ
- ht)
- finally return
- (if (not bp)
- ;; first run: main line
- rh
- ;; subsequent runs: grafts (value discarded)
- (setcdr bp (nconc
- ;; Maintain order of ‘leaves’.
- (let ((was (cdr bp)))
- (if (gnugo--nodep (car was))
- (list was)
- was))
- (list rh)))))))
- (setq tree (hang (pop leaves)))
- (mapc #'hang leaves)
- (>>tree tree))))
- (newline)
- (write-file filename)))))
-
-;;; gnugo.el ends here
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [elpa] master 6160089: * externals-list: Convert gnugo to :external,
Stefan Monnier <=