;;; 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.
;; 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.
;; 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
;; 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.
;;; 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.")
(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.")
"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
(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))
;;; 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.
(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.
(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."
(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."