comparison lisp/play/gomoku.el @ 88155:d7ddb3e565de

sync with trunk
author Henrik Enberg <henrik.enberg@telia.com>
date Mon, 16 Jan 2006 00:03:54 +0000
parents 0d8b17d428b5
children
comparison
equal deleted inserted replaced
88154:8ce476d3ba36 88155:d7ddb3e565de
1 ;;; gomoku.el --- Gomoku game between you and Emacs 1 ;;; gomoku.el --- Gomoku game between you and Emacs
2 2
3 ;; Copyright (C) 1988, 1994, 1996, 2001, 2003 Free Software Foundation, Inc. 3 ;; Copyright (C) 1988, 1994, 1996, 2001, 2002, 2003, 2004,
4 ;; 2005 Free Software Foundation, Inc.
4 5
5 ;; Author: Philippe Schnoebelen <phs@lsv.ens-cachan.fr> 6 ;; Author: Philippe Schnoebelen <phs@lsv.ens-cachan.fr>
6 ;; Maintainer: FSF 7 ;; Maintainer: FSF
7 ;; Adapted-By: ESR, Daniel Pfeiffer <occitan@esperanto.org> 8 ;; Adapted-By: ESR, Daniel Pfeiffer <occitan@esperanto.org>
8 ;; Keywords: games 9 ;; Keywords: games
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details. 21 ;; GNU General Public License for more details.
21 22
22 ;; You should have received a copy of the GNU General Public License 23 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the 24 ;; along with GNU Emacs; see the file COPYING. If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, 25 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 ;; Boston, MA 02111-1307, USA. 26 ;; Boston, MA 02110-1301, USA.
26 27
27 ;;; Commentary: 28 ;;; Commentary:
28 29
29 ;; RULES: 30 ;; RULES:
30 ;; 31 ;;
82 83
83 ;;; 84 ;;;
84 ;;; CONSTANTS FOR BOARD 85 ;;; CONSTANTS FOR BOARD
85 ;;; 86 ;;;
86 87
88 (defconst gomoku-buffer-name "*Gomoku*"
89 "Name of the Gomoku buffer.")
90
87 ;; You may change these values if you have a small screen or if the squares 91 ;; You may change these values if you have a small screen or if the squares
88 ;; look rectangular, but spacings SHOULD be at least 2 (MUST BE at least 1). 92 ;; look rectangular, but spacings SHOULD be at least 2 (MUST BE at least 1).
89 93
90 (defconst gomoku-square-width 4 94 (defconst gomoku-square-width 4
91 "*Horizontal spacing between squares on the Gomoku board.") 95 "*Horizontal spacing between squares on the Gomoku board.")
155 (define-key gomoku-mode-map [remap advertised-undo] 'gomoku-human-takes-back)) 159 (define-key gomoku-mode-map [remap advertised-undo] 'gomoku-human-takes-back))
156 160
157 (defvar gomoku-emacs-won () 161 (defvar gomoku-emacs-won ()
158 "For making font-lock use the winner's face for the line.") 162 "For making font-lock use the winner's face for the line.")
159 163
160 (defface gomoku-font-lock-O-face 164 (defface gomoku-O
161 '((((class color)) (:foreground "red" :weight bold))) 165 '((((class color)) (:foreground "red" :weight bold)))
162 "Face to use for Emacs' O." 166 "Face to use for Emacs' O."
163 :group 'gomoku) 167 :group 'gomoku)
164 168
165 (defface gomoku-font-lock-X-face 169 (defface gomoku-X
166 '((((class color)) (:foreground "green" :weight bold))) 170 '((((class color)) (:foreground "green" :weight bold)))
167 "Face to use for your X." 171 "Face to use for your X."
168 :group 'gomoku) 172 :group 'gomoku)
169 173
170 (defvar gomoku-font-lock-keywords 174 (defvar gomoku-font-lock-keywords
171 '(("O" . 'gomoku-font-lock-O-face) 175 '(("O" . 'gomoku-O)
172 ("X" . 'gomoku-font-lock-X-face) 176 ("X" . 'gomoku-X)
173 ("[-|/\\]" 0 (if gomoku-emacs-won 177 ("[-|/\\]" 0 (if gomoku-emacs-won 'gomoku-O 'gomoku-X)))
174 'gomoku-font-lock-O-face
175 'gomoku-font-lock-X-face)))
176 "*Font lock rules for Gomoku.") 178 "*Font lock rules for Gomoku.")
177 179
178 (put 'gomoku-mode 'front-sticky 180 (put 'gomoku-mode 'front-sticky
179 (put 'gomoku-mode 'rear-nonsticky '(intangible))) 181 (put 'gomoku-mode 'rear-nonsticky '(intangible)))
180 (put 'gomoku-mode 'intangible 1) 182 (put 'gomoku-mode 'intangible 1)
191 You play by moving the cursor over the square you choose and hitting \\[gomoku-human-plays]. 193 You play by moving the cursor over the square you choose and hitting \\[gomoku-human-plays].
192 194
193 Other useful commands: 195 Other useful commands:
194 \\{gomoku-mode-map} 196 \\{gomoku-mode-map}
195 Entry to this mode calls the value of `gomoku-mode-hook' if that value 197 Entry to this mode calls the value of `gomoku-mode-hook' if that value
196 is non-nil. One interesting value is `turn-on-font-lock'." 198 is non-nil."
197 (interactive) 199 (interactive)
200 (kill-all-local-variables)
198 (setq major-mode 'gomoku-mode 201 (setq major-mode 'gomoku-mode
199 mode-name "Gomoku") 202 mode-name "Gomoku")
200 (gomoku-display-statistics) 203 (gomoku-display-statistics)
201 (use-local-map gomoku-mode-map) 204 (use-local-map gomoku-mode-map)
202 (make-local-variable 'font-lock-defaults) 205 (make-local-variable 'font-lock-defaults)
203 (setq font-lock-defaults '(gomoku-font-lock-keywords t)) 206 (setq font-lock-defaults '(gomoku-font-lock-keywords t))
204 (toggle-read-only t) 207 (toggle-read-only t)
205 (run-hooks 'gomoku-mode-hook)) 208 (run-mode-hooks 'gomoku-mode-hook))
206 209
207 ;;; 210 ;;;
208 ;;; THE BOARD. 211 ;;; THE BOARD.
209 ;;; 212 ;;;
210 213
666 ((eq result 'emacs-won) 669 ((eq result 'emacs-won)
667 (setq gomoku-number-of-emacs-wins (1+ gomoku-number-of-emacs-wins)) 670 (setq gomoku-number-of-emacs-wins (1+ gomoku-number-of-emacs-wins))
668 (cond ((< gomoku-number-of-moves 20) 671 (cond ((< gomoku-number-of-moves 20)
669 "This was a REALLY QUICK win.") 672 "This was a REALLY QUICK win.")
670 (gomoku-human-refused-draw 673 (gomoku-human-refused-draw
671 "I won... Too bad you refused my offer of a draw !") 674 "I won... Too bad you refused my offer of a draw!")
672 (gomoku-human-took-back 675 (gomoku-human-took-back
673 "I won... Taking moves back will not help you !") 676 "I won... Taking moves back will not help you!")
674 ((not gomoku-emacs-played-first) 677 ((not gomoku-emacs-played-first)
675 "I won... Playing first did not help you much !") 678 "I won... Playing first did not help you much!")
676 ((and (zerop gomoku-number-of-human-wins) 679 ((and (zerop gomoku-number-of-human-wins)
677 (zerop gomoku-number-of-draws) 680 (zerop gomoku-number-of-draws)
678 (> gomoku-number-of-emacs-wins 1)) 681 (> gomoku-number-of-emacs-wins 1))
679 "I'm becoming tired of winning...") 682 "I'm becoming tired of winning...")
680 ("I won."))) 683 ("I won.")))
683 (concat "OK, you won this one." 686 (concat "OK, you won this one."
684 (cond 687 (cond
685 (gomoku-human-took-back 688 (gomoku-human-took-back
686 " I, for one, never take my moves back...") 689 " I, for one, never take my moves back...")
687 (gomoku-emacs-played-first 690 (gomoku-emacs-played-first
688 ".. so what ?") 691 ".. so what?")
689 (" Now, let me play first just once.")))) 692 (" Now, let me play first just once."))))
690 ((eq result 'human-resigned) 693 ((eq result 'human-resigned)
691 (setq gomoku-number-of-emacs-wins (1+ gomoku-number-of-emacs-wins)) 694 (setq gomoku-number-of-emacs-wins (1+ gomoku-number-of-emacs-wins))
692 "So you resign. That's just one more win for me.") 695 "So you resign. That's just one more win for me.")
693 ((eq result 'nobody-won) 696 ((eq result 'nobody-won)
745 748
746 Use \\[describe-mode] for more info." 749 Use \\[describe-mode] for more info."
747 (interactive (if current-prefix-arg 750 (interactive (if current-prefix-arg
748 (list (prefix-numeric-value current-prefix-arg) 751 (list (prefix-numeric-value current-prefix-arg)
749 (eval (read-minibuffer "Height: "))))) 752 (eval (read-minibuffer "Height: ")))))
750 (gomoku-switch-to-window) 753 ;; gomoku-switch-to-window, but without the potential call to gomoku
754 ;; from gomoku-prompt-for-other-game.
755 (if (get-buffer gomoku-buffer-name)
756 (switch-to-buffer gomoku-buffer-name)
757 (when gomoku-game-in-progress
758 (setq gomoku-emacs-is-computing nil)
759 (gomoku-terminate-game 'crash-game)
760 (sit-for 4)
761 (or (y-or-n-p "Another game? ") (error "Chicken!")))
762 (switch-to-buffer gomoku-buffer-name)
763 (gomoku-mode))
751 (cond 764 (cond
752 (gomoku-emacs-is-computing 765 (gomoku-emacs-is-computing
753 (gomoku-crash-game)) 766 (gomoku-crash-game))
754 ((or (not gomoku-game-in-progress) 767 ((or (not gomoku-game-in-progress)
755 (<= gomoku-number-of-moves 2)) 768 (<= gomoku-number-of-moves 2))
764 ((> n max-width) 777 ((> n max-width)
765 (error "I cannot display %d columns in that window" n))) 778 (error "I cannot display %d columns in that window" n)))
766 (if (and (> m max-height) 779 (if (and (> m max-height)
767 (not (eq m gomoku-saved-board-height)) 780 (not (eq m gomoku-saved-board-height))
768 ;; Use EQ because SAVED-BOARD-HEIGHT may be nil 781 ;; Use EQ because SAVED-BOARD-HEIGHT may be nil
769 (not (y-or-n-p (format "Do you really want %d rows " m)))) 782 (not (y-or-n-p (format "Do you really want %d rows? " m))))
770 (setq m max-height))) 783 (setq m max-height)))
771 (message "One moment, please...") 784 (message "One moment, please...")
772 (gomoku-start-game n m) 785 (gomoku-start-game n m)
773 (if (y-or-n-p "Do you allow me to play first ") 786 (if (y-or-n-p "Do you allow me to play first? ")
774 (gomoku-emacs-plays) 787 (gomoku-emacs-plays)
775 (gomoku-prompt-for-move))) 788 (gomoku-prompt-for-move)))
776 ((y-or-n-p "Shall we continue our game ") 789 ((y-or-n-p "Shall we continue our game? ")
777 (gomoku-prompt-for-move)) 790 (gomoku-prompt-for-move))
778 (t 791 (t
779 (gomoku-human-resigns)))) 792 (gomoku-human-resigns))))
780 793
781 (defun gomoku-emacs-plays () 794 (defun gomoku-emacs-plays ()
860 (gomoku-prompt-for-other-game)) 873 (gomoku-prompt-for-other-game))
861 (t 874 (t
862 (let (square score) 875 (let (square score)
863 (setq square (gomoku-point-square)) 876 (setq square (gomoku-point-square))
864 (cond ((null square) 877 (cond ((null square)
865 (error "Your point is not on a square. Retry !")) 878 (error "Your point is not on a square. Retry!"))
866 ((not (zerop (aref gomoku-board square))) 879 ((not (zerop (aref gomoku-board square)))
867 (error "Your point is not on a free square. Retry !")) 880 (error "Your point is not on a free square. Retry!"))
868 (t 881 (t
869 (setq score (aref gomoku-score-table square)) 882 (setq score (aref gomoku-score-table square))
870 (gomoku-play-move square 1) 883 (gomoku-play-move square 1)
871 (cond ((and (>= score gomoku-loosing-threshold) 884 (cond ((and (>= score gomoku-loosing-threshold)
872 ;; Just testing SCORE > THRESHOLD is not enough for 885 ;; Just testing SCORE > THRESHOLD is not enough for
887 ((not gomoku-game-in-progress) 900 ((not gomoku-game-in-progress)
888 (message "Too late for taking back...") 901 (message "Too late for taking back...")
889 (sit-for 4) 902 (sit-for 4)
890 (gomoku-prompt-for-other-game)) 903 (gomoku-prompt-for-other-game))
891 ((zerop gomoku-number-of-human-moves) 904 ((zerop gomoku-number-of-human-moves)
892 (message "You have not played yet... Your move ?")) 905 (message "You have not played yet... Your move?"))
893 (t 906 (t
894 (message "One moment, please...") 907 (message "One moment, please...")
895 ;; It is possible for the user to let Emacs play several consecutive 908 ;; It is possible for the user to let Emacs play several consecutive
896 ;; moves, so that the best way to know when to stop taking back moves is 909 ;; moves, so that the best way to know when to stop taking back moves is
897 ;; to count the number of human moves: 910 ;; to count the number of human moves:
908 (cond 921 (cond
909 (gomoku-emacs-is-computing 922 (gomoku-emacs-is-computing
910 (gomoku-crash-game)) 923 (gomoku-crash-game))
911 ((not gomoku-game-in-progress) 924 ((not gomoku-game-in-progress)
912 (message "There is no game in progress")) 925 (message "There is no game in progress"))
913 ((y-or-n-p "You mean, you resign ") 926 ((y-or-n-p "You mean, you resign? ")
914 (gomoku-terminate-game 'human-resigned)) 927 (gomoku-terminate-game 'human-resigned))
915 ((y-or-n-p "You mean, we continue ") 928 ((y-or-n-p "You mean, we continue? ")
916 (gomoku-prompt-for-move)) 929 (gomoku-prompt-for-move))
917 (t 930 (t
918 (gomoku-terminate-game 'human-resigned)))) ; OK. Accept it 931 (gomoku-terminate-game 'human-resigned)))) ; OK. Accept it
919 932
920 ;;; 933 ;;;
922 ;;; 935 ;;;
923 936
924 (defun gomoku-prompt-for-move () 937 (defun gomoku-prompt-for-move ()
925 "Display a message asking for Human's move." 938 "Display a message asking for Human's move."
926 (message (if (zerop gomoku-number-of-human-moves) 939 (message (if (zerop gomoku-number-of-human-moves)
927 "Your move ? (move to a free square and hit X, RET ...)" 940 "Your move? (move to a free square and hit X, RET ...)"
928 "Your move ?")) 941 "Your move?"))
929 ;; This may seem silly, but if one omits the following line (or a similar 942 ;; This may seem silly, but if one omits the following line (or a similar
930 ;; one), the cursor may very well go to some place where POINT is not. 943 ;; one), the cursor may very well go to some place where POINT is not.
931 (save-excursion (set-buffer (other-buffer)))) 944 (save-excursion (set-buffer (other-buffer))))
932 945
933 (defun gomoku-prompt-for-other-game () 946 (defun gomoku-prompt-for-other-game ()
934 "Ask for another game, and start it." 947 "Ask for another game, and start it."
935 (if (y-or-n-p "Another game ") 948 (if (y-or-n-p "Another game? ")
936 (gomoku gomoku-board-width gomoku-board-height) 949 (gomoku gomoku-board-width gomoku-board-height)
937 (message "Chicken !"))) 950 (error "Chicken!")))
938 951
939 (defun gomoku-offer-a-draw () 952 (defun gomoku-offer-a-draw ()
940 "Offer a draw and return t if Human accepted it." 953 "Offer a draw and return t if Human accepted it."
941 (or (y-or-n-p "I offer you a draw. Do you accept it ") 954 (or (y-or-n-p "I offer you a draw. Do you accept it? ")
942 (not (setq gomoku-human-refused-draw t)))) 955 (not (setq gomoku-human-refused-draw t))))
943 956
944 ;;; 957 ;;;
945 ;;; DISPLAYING THE BOARD. 958 ;;; DISPLAYING THE BOARD.
946 ;;; 959 ;;;
1065 (force-mode-line-update)) 1078 (force-mode-line-update))
1066 1079
1067 (defun gomoku-switch-to-window () 1080 (defun gomoku-switch-to-window ()
1068 "Find or create the Gomoku buffer, and display it." 1081 "Find or create the Gomoku buffer, and display it."
1069 (interactive) 1082 (interactive)
1070 (let ((buff (get-buffer "*Gomoku*"))) 1083 (if (get-buffer gomoku-buffer-name) ; Buffer exists:
1071 (if buff ; Buffer exists: 1084 (switch-to-buffer gomoku-buffer-name) ; no problem.
1072 (switch-to-buffer buff) ; no problem. 1085 (if gomoku-game-in-progress
1073 (if gomoku-game-in-progress 1086 (gomoku-crash-game)) ; buffer has been killed or something
1074 (gomoku-crash-game)) ; buffer has been killed or something 1087 (switch-to-buffer gomoku-buffer-name) ; Anyway, start anew.
1075 (switch-to-buffer "*Gomoku*") ; Anyway, start anew. 1088 (gomoku-mode)))
1076 (gomoku-mode))))
1077 1089
1078 ;;; 1090 ;;;
1079 ;;; CROSSING WINNING QTUPLES. 1091 ;;; CROSSING WINNING QTUPLES.
1080 ;;; 1092 ;;;
1081 1093
1197 (move-to-column (+ gomoku-x-offset 1209 (move-to-column (+ gomoku-x-offset
1198 (* gomoku-square-width (1- gomoku-board-width))))) 1210 (* gomoku-square-width (1- gomoku-board-width)))))
1199 1211
1200 (provide 'gomoku) 1212 (provide 'gomoku)
1201 1213
1214 ;;; arch-tag: b1b8205e-77fc-4597-b373-3ea2c04311eb
1202 ;;; gomoku.el ends here 1215 ;;; gomoku.el ends here