Update FSF's address.
[bpt/emacs.git] / lisp / play / gomoku.el
index 75344c8..b7d6bea 100644 (file)
@@ -1,8 +1,8 @@
 ;;; gomoku.el --- Gomoku game between you and Emacs
 
-;; Copyright (C) 1988 Free Software Foundation, Inc.
+;; Copyright (C) 1988, 1994 Free Software Foundation, Inc.
 
-;; Author: Phillippe Schnoebelen <phs@lifia.imag.fr>
+;; Author: Philippe Schnoebelen <phs@lifia.imag.fr>
 ;; Adapted-By: ESR
 ;; Keywords: games
 
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to
-;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
 
 ;;; Commentary:
 
-;;; Gomoku game between you and GNU Emacs.  Last modified on 13 Sep 1988
-;;;
-;;; Written by Ph. Schnoebelen (phs@lifia.imag.fr), 1987, 1988
-;;; with precious advices from J.-F. Rit.
-;;; This has been tested with GNU Emacs 18.50.
+;; Gomoku game between you and GNU Emacs.  Last modified on 13 Sep 1988
+;;
+;; Written by Ph. Schnoebelen (phs@lifia.imag.fr), 1987, 1988
+;; with precious advices from J.-F. Rit.
+;; This has been tested with GNU Emacs 18.50.
 
 ;; RULES:
 ;;
 ;; it yourself :-).
 
 
-;; HOW TO INSTALL:
-;;
-;; There is nothing specific w.r.t. installation: just put this file in the
-;; lisp directory and add an autoload for command gomoku in site-init.el. If
-;; you don't want to rebuild Emacs, then every single user interested in
-;; Gomoku will have to put the autoload command in its .emacs file.  Another
-;; possibility is to define in your .emacs some command using (require
-;; 'gomoku).
-;;
-;; The most important thing is to BYTE-COMPILE gomoku.el because it is
-;; important that the code be as fast as possible.
-;;
 ;; There are two main places where you may want to customize the program: key
 ;; bindings and board display. These features are commented in the code. Go
 ;; and see.
@@ -63,7 +52,7 @@
 
 ;; HOW TO USE:
 ;;
-;; Once this file has been installed, the command "M-x gomoku" will display a
+;; The command "M-x gomoku" displays a
 ;; board, the size of which depends on the size of the current window. The
 ;; size of the board is easily modified by giving numeric arguments to the
 ;; gomoku command and/or by customizing the displaying parameters.
   (define-key gomoku-mode-map "X" 'gomoku-human-plays)         ; X
   (define-key gomoku-mode-map "x" 'gomoku-human-plays)         ; x
   (define-key gomoku-mode-map "\C-m" 'gomoku-human-plays)      ; RET
-  (define-key gomoku-mode-map "\C-cp" 'gomoku-human-plays)     ; C-C P
-  (define-key gomoku-mode-map "\C-cb" 'gomoku-human-takes-back) ; C-C B
-  (define-key gomoku-mode-map "\C-cr" 'gomoku-human-resigns)   ; C-C R
-  (define-key gomoku-mode-map "\C-ce" 'gomoku-emacs-plays)     ; C-C E
-
-  ;; Key bindings for "function" keys. If your terminal has such
-  ;; keys, make sure they are declared through the function-keymap
-  ;; keymap (see file keypad.el).
-  ;; One problem with keypad.el is that the function-key-sequence
-  ;; function is really slow, so slow that you may want to comment out
-  ;; the following lines ...
-  (if (featurep 'keypad)
-      (let (keys)
-       (if (setq keys (function-key-sequence ?u))              ; Up Arrow
-           (define-key gomoku-mode-map keys 'gomoku-move-up))
-       (if (setq keys (function-key-sequence ?d))              ; Down Arrow
-           (define-key gomoku-mode-map keys 'gomoku-move-down))
-       (if (setq keys (function-key-sequence ?l))              ; Left Arrow
-           (define-key gomoku-mode-map keys 'gomoku-move-left))
-       (if (setq keys (function-key-sequence ?r))              ; Right Arrow
-           (define-key gomoku-mode-map keys 'gomoku-move-right))
-;;     (if (setq keys (function-key-sequence ?e))              ; Enter
-;;         (define-key gomoku-mode-map keys 'gomoku-human-plays))
-;;     (if (setq keys (function-key-sequence ?I))              ; Insert
-;;         (define-key gomoku-mode-map keys 'gomoku-human-plays))
-       )))
-
-
+  (define-key gomoku-mode-map "\C-c\C-p" 'gomoku-human-plays)  ; C-C C-P
+  (define-key gomoku-mode-map "\C-c\C-b" 'gomoku-human-takes-back) ; C-C C-B
+  (define-key gomoku-mode-map "\C-c\C-r" 'gomoku-human-resigns)        ; C-C C-R
+  (define-key gomoku-mode-map "\C-c\C-e" 'gomoku-emacs-plays)  ; C-C C-E
+
+  (define-key gomoku-mode-map [up] 'gomoku-move-up)
+  (define-key gomoku-mode-map [down] 'gomoku-move-down)
+  (define-key gomoku-mode-map [left] 'gomoku-move-left)
+  (define-key gomoku-mode-map [right] 'gomoku-move-right)
+  (define-key gomoku-mode-map [kp-enter] 'gomoku-human-plays)
+  (define-key gomoku-mode-map [mouse-2] 'gomoku-click)
+  (define-key gomoku-mode-map [insert] 'gomoku-human-plays))
 
 (defun gomoku-mode ()
   "Major mode for playing Gomoku against Emacs.
@@ -294,8 +267,8 @@ is non-nil."
 ;; please send me a note. Thanks.
 
 
-;; As we choosed values 0, 1 and 6 to denote empty, X and O squares, the
-;; contents of a qtuple is uniquely determined by the sum of its elements and
+;; As we chose values 0, 1 and 6 to denote empty, X and O squares, the
+;; contents of a qtuple are uniquely determined by the sum of its elements and
 ;; we just have to set up a translation table.
 
 (defconst gomoku-score-trans-table
@@ -348,16 +321,11 @@ is non-nil."
        ;; If score is equally good, choose randomly. But first check freeness:
        ((not (zerop (aref gomoku-board square)))
        (aset gomoku-score-table square -1))
-       ((= count (random-number (setq count (1+ count))))
+       ((zerop (random (setq count (1+ count))))
        (setq best-square square
              score-max   score)))
       (setq square (1+ square)))       ; try next square
     best-square))
-
-(defun random-number (n)
-  "Return a random integer between 0 and N-1 inclusive."
-  (setq n (% (random) n))
-  (if (< n 0) (- n) n))
 \f
 ;;;
 ;;; INITIALIZING THE SCORE TABLE.
@@ -612,11 +580,11 @@ that DVAL has been added on SQUARE."
 ;;; SESSION CONTROL.
 ;;;
 
-(defvar gomoku-number-of-wins 0
-  "Number of games already won in this session.")
+(defvar gomoku-number-of-emacs-wins 0
+  "Number of games Emacs won in this session.")
 
-(defvar gomoku-number-of-losses 0
-  "Number of games already lost in this session.")
+(defvar gomoku-number-of-human-wins 0
+  "Number of games you won in this session.")
 
 (defvar gomoku-number-of-draws 0
   "Number of games already drawn in this session.")
@@ -627,7 +595,7 @@ that DVAL has been added on SQUARE."
   (let (message)
     (cond
      ((eq result 'emacs-won)
-      (setq gomoku-number-of-wins (1+ gomoku-number-of-wins))
+      (setq gomoku-number-of-emacs-wins (1+ gomoku-number-of-emacs-wins))
       (setq message
            (cond ((< gomoku-number-of-moves 20)
                   "This was a REALLY QUICK win.")
@@ -637,14 +605,14 @@ that DVAL has been added on SQUARE."
                   "I won... Taking moves back will not help you !")
                  ((not gomoku-emacs-played-first)
                   "I won... Playing first did not help you much !")
-                 ((and (zerop gomoku-number-of-losses)
+                 ((and (zerop gomoku-number-of-human-wins)
                        (zerop gomoku-number-of-draws)
-                       (> gomoku-number-of-wins 1))
+                       (> gomoku-number-of-emacs-wins 1))
                   "I'm becoming tired of winning...")
                  (t
                   "I won."))))
      ((eq result 'human-won)
-      (setq gomoku-number-of-losses (1+ gomoku-number-of-losses))
+      (setq gomoku-number-of-human-wins (1+ gomoku-number-of-human-wins))
       (setq message
            (cond
             (gomoku-human-took-back
@@ -654,7 +622,7 @@ that DVAL has been added on SQUARE."
             (t
              "OK, you won this one.  Now, let me play first just once."))))
      ((eq result 'human-resigned)
-      (setq gomoku-number-of-wins (1+ gomoku-number-of-wins))
+      (setq gomoku-number-of-emacs-wins (1+ gomoku-number-of-emacs-wins))
       (setq message "So you resign.  That's just one more win for me."))
      ((eq result 'nobody-won)
       (setq gomoku-number-of-draws (1+ gomoku-number-of-draws))
@@ -696,6 +664,7 @@ that DVAL has been added on SQUARE."
 ;;; INTERACTIVE COMMANDS.
 ;;;
 
+;;;###autoload
 (defun gomoku (&optional n m)
   "Start a Gomoku game between you and Emacs.
 If a game is in progress, this command allow you to resume it.
@@ -770,6 +739,12 @@ Use \\[describe-mode] for more info."
                   (t
                    (gomoku-prompt-for-move)))))))))
 
+(defun gomoku-click (click)
+  "Play at the square where you click."
+  (interactive "e")
+  (mouse-set-point click)
+  (gomoku-human-plays))
+
 (defun gomoku-human-plays ()
   "Signal to the Gomoku program that you have played.
 You must have put the cursor on the square where you want to play.
@@ -943,43 +918,41 @@ If the game is finished, this command requests for another game."
 
 (defun gomoku-put-char (char)
   "Draw CHAR on the Gomoku screen."
-  (if buffer-read-only (toggle-read-only))
-  (insert char)
-  (delete-char 1)
-  (backward-char 1)
-  (toggle-read-only))
+  (let ((inhibit-read-only t))
+    (insert char)
+    (delete-char 1)
+    (backward-char 1)))
 
 (defun gomoku-init-display (n m)
   "Display an N by M Gomoku board."
   (buffer-disable-undo (current-buffer))
-  (if buffer-read-only (toggle-read-only))
-  (erase-buffer)
-  (let (string1 string2 string3 string4)
-    ;; We do not use gomoku-plot-square which would be too slow for
-    ;; initializing the display. Rather we build STRING1 for lines where
-    ;; board squares are to be found, and STRING2 for empty lines. STRING1 is
-    ;; like STRING2 except for dots every DX squares. Empty lines are filled
-    ;; with spaces so that cursor moving up and down remains on the same
-    ;; column.
-    (setq string1 (concat (make-string (1- gomoku-square-width) ? ) ".")
-         string1 (apply 'concat
-                   (make-list (1- n) string1))
-         string1 (concat (make-string gomoku-x-offset ? ) "." string1 "\n")
-         string2 (make-string (+ 1 gomoku-x-offset
-                                 (* (1- n) gomoku-square-width))
-                              ? )
-         string2 (concat string2 "\n")
-         string3 (apply 'concat
-                   (make-list (1- gomoku-square-height) string2))
-         string3 (concat string3 string1)
-         string3 (apply 'concat
-                   (make-list (1- m) string3))
-         string4 (apply 'concat
-                   (make-list gomoku-y-offset string2)))
-    (insert string4 string1 string3))
-  (toggle-read-only)
-  (gomoku-goto-xy (/ (1+ n) 2) (/ (1+ m) 2)) ; center of the board
-  (sit-for 0))                         ; Display NOW
+  (let ((inhibit-read-only t))
+    (erase-buffer)
+    (let (string1 string2 string3 string4)
+      ;; We do not use gomoku-plot-square which would be too slow for
+      ;; initializing the display. Rather we build STRING1 for lines where
+      ;; board squares are to be found, and STRING2 for empty lines. STRING1 is
+      ;; like STRING2 except for dots every DX squares. Empty lines are filled
+      ;; with spaces so that cursor moving up and down remains on the same
+      ;; column.
+      (setq string1 (concat (make-string (1- gomoku-square-width) ? ) ".")
+           string1 (apply 'concat
+                          (make-list (1- n) string1))
+           string1 (concat (make-string gomoku-x-offset ? ) "." string1 "\n")
+           string2 (make-string (+ 1 gomoku-x-offset
+                                   (* (1- n) gomoku-square-width))
+                                ? )
+           string2 (concat string2 "\n")
+           string3 (apply 'concat
+                          (make-list (1- gomoku-square-height) string2))
+           string3 (concat string3 string1)
+           string3 (apply 'concat
+                          (make-list (1- m) string3))
+           string4 (apply 'concat
+                          (make-list gomoku-y-offset string2)))
+      (insert string4 string1 string3))
+    (gomoku-goto-xy (/ (1+ n) 2) (/ (1+ m) 2)) ; center of the board
+    (sit-for 0)))                      ; Display NOW
 
 (defun gomoku-display-statistics ()
   "Obnoxiously display some statistics about previous games in mode line."
@@ -989,22 +962,14 @@ If the game is finished, this command requests for another game."
        (cond
         ((not (zerop gomoku-number-of-draws))
          (format ": Won %d, lost %d, drew %d"
-                 gomoku-number-of-wins
-                 gomoku-number-of-losses
+                 gomoku-number-of-human-wins
+                 gomoku-number-of-emacs-wins
                  gomoku-number-of-draws))
-        ((not (zerop gomoku-number-of-losses))
-         (format ": Won %d, lost %d"
-                 gomoku-number-of-wins
-                 gomoku-number-of-losses))
-        ((zerop gomoku-number-of-wins)
-         "")
-        ((= 1 gomoku-number-of-wins)
-         ": Already won one")
         (t
-         (format ": Won %d in a row"
-                 gomoku-number-of-wins))))
-  ;; Then a (standard) kludgy line will force update of mode line.
-  (set-buffer-modified-p (buffer-modified-p)))
+         (format ": Won %d, lost %d"
+                 gomoku-number-of-human-wins
+                 gomoku-number-of-emacs-wins))))
+  (force-mode-line-update))
 
 (defun gomoku-switch-to-window ()
   "Find or create the Gomoku buffer, and display it."