diff options
| author | Richard M. Stallman | 1996-04-05 19:38:42 +0000 |
|---|---|---|
| committer | Richard M. Stallman | 1996-04-05 19:38:42 +0000 |
| commit | d7ab27189363acf15d7676085d552474435d7571 (patch) | |
| tree | 816cbab0954c4ec8d5a932601366a6c9da595859 | |
| parent | cb2e51f84b4bc68f1493da1b9883143409058140 (diff) | |
| download | emacs-d7ab27189363acf15d7676085d552474435d7571.tar.gz emacs-d7ab27189363acf15d7676085d552474435d7571.zip | |
(gomoku-click): Position on nearest square. Adapt keymap accordingly.
(gomoku-mouse-play): Use it to play nearest to mouse click.
(gomoku-terminate-game): Factorize messages.
(gomoku): Allow interactive passing of board size. Don't make a fuss
about restarting a game that hasn't progressed.
(gomoku-offer-a-draw): Give user the choice it pretended to give.
(gomoku-point-x): Deleted function.
(gomoku-point-y, gomoku-point-square): Simplified because point is
always on a square.
(gomoku-goto-xy, gomoku-plot-square): Fix line count due to
intangible newlines.
(gomoku-init-display): Once again fairly fast due to minimization of
characters in buffer and text-property operations. Cursor cannot be
be off a square.
(gomoku-display-statistics): Simplified equivalently.
(gomoku-winning-qtuple-beg, gomoku-winning-qtuple-end)
(gomoku-winning-qtuple-dx, gomoku-winning-qtuple-dy): Pseudo variables
only used for non-functional argument passing deleted.
(gomoku-cross-winning-qtuple): Accordingly deleted function and
(gomoku-check-filled-qtuple): Accordingly adapted.
(gomoku-cross-qtuple): Don't be confused by tabs.
(gomoku-move-down, gomoku-move-up): Simplified because point is always
on square.
(gomoku-beginning-of-line, gomoku-end-of-line): New commands necessary
because intangible newlines perverted these.
| -rw-r--r-- | lisp/play/gomoku.el | 392 |
1 files changed, 206 insertions, 186 deletions
diff --git a/lisp/play/gomoku.el b/lisp/play/gomoku.el index 104f3989a40..332d1cedd9c 100644 --- a/lisp/play/gomoku.el +++ b/lisp/play/gomoku.el | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | ;; Copyright (C) 1988, 1994, 1996 Free Software Foundation, Inc. | 3 | ;; Copyright (C) 1988, 1994, 1996 Free Software Foundation, Inc. |
| 4 | 4 | ||
| 5 | ;; Author: Philippe Schnoebelen <phs@lifia.imag.fr> | 5 | ;; Author: Philippe Schnoebelen <phs@lifia.imag.fr> |
| 6 | ;; Adapted-By: ESR | 6 | ;; Adapted-By: ESR, Daniel.Pfeiffer@Informatik.START.dbp.de |
| 7 | ;; Keywords: games | 7 | ;; Keywords: games |
| 8 | 8 | ||
| 9 | ;; This file is part of GNU Emacs. | 9 | ;; This file is part of GNU Emacs. |
| @@ -101,7 +101,6 @@ | |||
| 101 | (define-key gomoku-mode-map "\C-p" 'gomoku-move-up) ; C-p | 101 | (define-key gomoku-mode-map "\C-p" 'gomoku-move-up) ; C-p |
| 102 | 102 | ||
| 103 | ;; Key bindings for entering Human moves. | 103 | ;; Key bindings for entering Human moves. |
| 104 | ;; If you have a mouse, you may also bind some mouse click ... | ||
| 105 | (define-key gomoku-mode-map "X" 'gomoku-human-plays) ; X | 104 | (define-key gomoku-mode-map "X" 'gomoku-human-plays) ; X |
| 106 | (define-key gomoku-mode-map "x" 'gomoku-human-plays) ; x | 105 | (define-key gomoku-mode-map "x" 'gomoku-human-plays) ; x |
| 107 | (define-key gomoku-mode-map " " 'gomoku-human-plays) ; SPC | 106 | (define-key gomoku-mode-map " " 'gomoku-human-plays) ; SPC |
| @@ -112,13 +111,22 @@ | |||
| 112 | (define-key gomoku-mode-map "\C-c\C-e" 'gomoku-emacs-plays) ; C-c C-e | 111 | (define-key gomoku-mode-map "\C-c\C-e" 'gomoku-emacs-plays) ; C-c C-e |
| 113 | 112 | ||
| 114 | (define-key gomoku-mode-map [kp-enter] 'gomoku-human-plays) | 113 | (define-key gomoku-mode-map [kp-enter] 'gomoku-human-plays) |
| 115 | (define-key gomoku-mode-map [mouse-2] 'gomoku-click) | ||
| 116 | (define-key gomoku-mode-map [insert] 'gomoku-human-plays) | 114 | (define-key gomoku-mode-map [insert] 'gomoku-human-plays) |
| 115 | (define-key gomoku-mode-map [down-mouse-1] 'gomoku-click) | ||
| 116 | (define-key gomoku-mode-map [drag-mouse-1] 'gomoku-click) | ||
| 117 | (define-key gomoku-mode-map [mouse-1] 'gomoku-click) | ||
| 118 | (define-key gomoku-mode-map [down-mouse-2] 'gomoku-click) | ||
| 119 | (define-key gomoku-mode-map [mouse-2] 'gomoku-mouse-play) | ||
| 120 | (define-key gomoku-mode-map [drag-mouse-2] 'gomoku-mouse-play) | ||
| 117 | 121 | ||
| 118 | (substitute-key-definition 'previous-line 'gomoku-move-up | 122 | (substitute-key-definition 'previous-line 'gomoku-move-up |
| 119 | gomoku-mode-map (current-global-map)) | 123 | gomoku-mode-map (current-global-map)) |
| 120 | (substitute-key-definition 'next-line 'gomoku-move-down | 124 | (substitute-key-definition 'next-line 'gomoku-move-down |
| 121 | gomoku-mode-map (current-global-map)) | 125 | gomoku-mode-map (current-global-map)) |
| 126 | (substitute-key-definition 'beginning-of-line 'gomoku-beginning-of-line | ||
| 127 | gomoku-mode-map (current-global-map)) | ||
| 128 | (substitute-key-definition 'end-of-line 'gomoku-end-of-line | ||
| 129 | gomoku-mode-map (current-global-map)) | ||
| 122 | (substitute-key-definition 'undo 'gomoku-human-takes-back | 130 | (substitute-key-definition 'undo 'gomoku-human-takes-back |
| 123 | gomoku-mode-map (current-global-map)) | 131 | gomoku-mode-map (current-global-map)) |
| 124 | (substitute-key-definition 'advertised-undo 'gomoku-human-takes-back | 132 | (substitute-key-definition 'advertised-undo 'gomoku-human-takes-back |
| @@ -147,6 +155,7 @@ | |||
| 147 | 155 | ||
| 148 | (put 'gomoku-mode 'front-sticky | 156 | (put 'gomoku-mode 'front-sticky |
| 149 | (put 'gomoku-mode 'rear-nonsticky '(intangible))) | 157 | (put 'gomoku-mode 'rear-nonsticky '(intangible))) |
| 158 | (put 'gomoku-mode 'intangible 1) | ||
| 150 | 159 | ||
| 151 | (defun gomoku-mode () | 160 | (defun gomoku-mode () |
| 152 | "Major mode for playing Gomoku against Emacs. | 161 | "Major mode for playing Gomoku against Emacs. |
| @@ -627,66 +636,58 @@ that DVAL has been added on SQUARE." | |||
| 627 | 636 | ||
| 628 | (defun gomoku-terminate-game (result) | 637 | (defun gomoku-terminate-game (result) |
| 629 | "Terminate the current game with RESULT." | 638 | "Terminate the current game with RESULT." |
| 630 | (let (message) | 639 | (message |
| 631 | (cond | 640 | (cond |
| 632 | ((eq result 'emacs-won) | 641 | ((eq result 'emacs-won) |
| 633 | (setq gomoku-number-of-emacs-wins (1+ gomoku-number-of-emacs-wins)) | 642 | (setq gomoku-number-of-emacs-wins (1+ gomoku-number-of-emacs-wins)) |
| 634 | (setq message | 643 | (cond ((< gomoku-number-of-moves 20) |
| 635 | (cond ((< gomoku-number-of-moves 20) | 644 | "This was a REALLY QUICK win.") |
| 636 | "This was a REALLY QUICK win.") | 645 | (gomoku-human-refused-draw |
| 637 | (gomoku-human-refused-draw | 646 | "I won... Too bad you refused my offer of a draw !") |
| 638 | "I won... Too bad you refused my offer of a draw !") | 647 | (gomoku-human-took-back |
| 639 | (gomoku-human-took-back | 648 | "I won... Taking moves back will not help you !") |
| 640 | "I won... Taking moves back will not help you !") | 649 | ((not gomoku-emacs-played-first) |
| 641 | ((not gomoku-emacs-played-first) | 650 | "I won... Playing first did not help you much !") |
| 642 | "I won... Playing first did not help you much !") | 651 | ((and (zerop gomoku-number-of-human-wins) |
| 643 | ((and (zerop gomoku-number-of-human-wins) | 652 | (zerop gomoku-number-of-draws) |
| 644 | (zerop gomoku-number-of-draws) | 653 | (> gomoku-number-of-emacs-wins 1)) |
| 645 | (> gomoku-number-of-emacs-wins 1)) | 654 | "I'm becoming tired of winning...") |
| 646 | "I'm becoming tired of winning...") | 655 | ("I won."))) |
| 647 | (t | 656 | ((eq result 'human-won) |
| 648 | "I won.")))) | 657 | (setq gomoku-number-of-human-wins (1+ gomoku-number-of-human-wins)) |
| 649 | ((eq result 'human-won) | 658 | (concat "OK, you won this one." |
| 650 | (setq gomoku-number-of-human-wins (1+ gomoku-number-of-human-wins)) | 659 | (cond |
| 651 | (setq message | 660 | (gomoku-human-took-back |
| 652 | (cond | 661 | " I, for one, never take my moves back...") |
| 653 | (gomoku-human-took-back | 662 | (gomoku-emacs-played-first |
| 654 | "OK, you won this one. I, for one, never take my moves back...") | 663 | ".. so what ?") |
| 655 | (gomoku-emacs-played-first | 664 | (" Now, let me play first just once.")))) |
| 656 | "OK, you won this one... so what ?") | 665 | ((eq result 'human-resigned) |
| 657 | (t | 666 | (setq gomoku-number-of-emacs-wins (1+ gomoku-number-of-emacs-wins)) |
| 658 | "OK, you won this one. Now, let me play first just once.")))) | 667 | "So you resign. That's just one more win for me.") |
| 659 | ((eq result 'human-resigned) | 668 | ((eq result 'nobody-won) |
| 660 | (setq gomoku-number-of-emacs-wins (1+ gomoku-number-of-emacs-wins)) | 669 | (setq gomoku-number-of-draws (1+ gomoku-number-of-draws)) |
| 661 | (setq message "So you resign. That's just one more win for me.")) | 670 | (concat "This is a draw. " |
| 662 | ((eq result 'nobody-won) | 671 | (cond |
| 663 | (setq gomoku-number-of-draws (1+ gomoku-number-of-draws)) | 672 | (gomoku-human-took-back |
| 664 | (setq message | 673 | "I, for one, never take my moves back...") |
| 665 | (cond | 674 | (gomoku-emacs-played-first |
| 666 | (gomoku-human-took-back | 675 | "Just chance, I guess.") |
| 667 | "This is a draw. I, for one, never take my moves back...") | 676 | ("Now, let me play first just once.")))) |
| 668 | (gomoku-emacs-played-first | 677 | ((eq result 'draw-agreed) |
| 669 | "This is a draw. Just chance, I guess.") | 678 | (setq gomoku-number-of-draws (1+ gomoku-number-of-draws)) |
| 670 | (t | 679 | (concat "Draw agreed. " |
| 671 | "This is a draw. Now, let me play first just once.")))) | 680 | (cond |
| 672 | ((eq result 'draw-agreed) | 681 | (gomoku-human-took-back |
| 673 | (setq gomoku-number-of-draws (1+ gomoku-number-of-draws)) | 682 | "I, for one, never take my moves back...") |
| 674 | (setq message | 683 | (gomoku-emacs-played-first |
| 675 | (cond | 684 | "You were lucky.") |
| 676 | (gomoku-human-took-back | 685 | ("Now, let me play first just once.")))) |
| 677 | "Draw agreed. I, for one, never take my moves back...") | 686 | ((eq result 'crash-game) |
| 678 | (gomoku-emacs-played-first | 687 | "Sorry, I have been interrupted and cannot resume that game..."))) |
| 679 | "Draw agreed. You were lucky.") | 688 | (gomoku-display-statistics) |
| 680 | (t | 689 | ;;(ding) |
| 681 | "Draw agreed. Now, let me play first just once.")))) | 690 | (setq gomoku-game-in-progress nil)) |
| 682 | ((eq result 'crash-game) | ||
| 683 | (setq message | ||
| 684 | "Sorry, I have been interrupted and cannot resume that game..."))) | ||
| 685 | |||
| 686 | (gomoku-display-statistics) | ||
| 687 | (if message (message message)) | ||
| 688 | ;;(ding) | ||
| 689 | (setq gomoku-game-in-progress nil))) | ||
| 690 | 691 | ||
| 691 | (defun gomoku-crash-game () | 692 | (defun gomoku-crash-game () |
| 692 | "What to do when Emacs detects it has been interrupted." | 693 | "What to do when Emacs detects it has been interrupted." |
| @@ -704,6 +705,7 @@ that DVAL has been added on SQUARE." | |||
| 704 | "Start a Gomoku game between you and Emacs. | 705 | "Start a Gomoku game between you and Emacs. |
| 705 | If a game is in progress, this command allow you to resume it. | 706 | If a game is in progress, this command allow you to resume it. |
| 706 | If optional arguments N and M are given, an N by M board is used. | 707 | If optional arguments N and M are given, an N by M board is used. |
| 708 | If prefix arg is given for N, M is prompted for. | ||
| 707 | 709 | ||
| 708 | You and Emacs play in turn by marking a free square. You mark it with X | 710 | You and Emacs play in turn by marking a free square. You mark it with X |
| 709 | and Emacs marks it with O. The winner is the first to get five contiguous | 711 | and Emacs marks it with O. The winner is the first to get five contiguous |
| @@ -712,12 +714,15 @@ marks horizontally, vertically or in diagonal. | |||
| 712 | You play by moving the cursor over the square you choose and hitting | 714 | You play by moving the cursor over the square you choose and hitting |
| 713 | \\<gomoku-mode-map>\\[gomoku-human-plays]. | 715 | \\<gomoku-mode-map>\\[gomoku-human-plays]. |
| 714 | Use \\[describe-mode] for more info." | 716 | Use \\[describe-mode] for more info." |
| 715 | (interactive) | 717 | (interactive (if current-prefix-arg |
| 718 | (list (prefix-numeric-value current-prefix-arg) | ||
| 719 | (eval (read-minibuffer "Height: "))))) | ||
| 716 | (gomoku-switch-to-window) | 720 | (gomoku-switch-to-window) |
| 717 | (cond | 721 | (cond |
| 718 | (gomoku-emacs-is-computing | 722 | (gomoku-emacs-is-computing |
| 719 | (gomoku-crash-game)) | 723 | (gomoku-crash-game)) |
| 720 | ((not gomoku-game-in-progress) | 724 | ((or (not gomoku-game-in-progress) |
| 725 | (<= gomoku-number-of-moves 2)) | ||
| 721 | (let ((max-width (gomoku-max-width)) | 726 | (let ((max-width (gomoku-max-width)) |
| 722 | (max-height (gomoku-max-height))) | 727 | (max-height (gomoku-max-height))) |
| 723 | (or n (setq n max-width)) | 728 | (or n (setq n max-width)) |
| @@ -729,8 +734,8 @@ Use \\[describe-mode] for more info." | |||
| 729 | ((> n max-width) | 734 | ((> n max-width) |
| 730 | (error "I cannot display %d columns in that window" n))) | 735 | (error "I cannot display %d columns in that window" n))) |
| 731 | (if (and (> m max-height) | 736 | (if (and (> m max-height) |
| 732 | (not (equal m gomoku-saved-board-height)) | 737 | (not (eq m gomoku-saved-board-height)) |
| 733 | ;; Use EQUAL because SAVED-BOARD-HEIGHT may be nil | 738 | ;; Use EQ because SAVED-BOARD-HEIGHT may be nil |
| 734 | (not (y-or-n-p (format "Do you really want %d rows " m)))) | 739 | (not (y-or-n-p (format "Do you really want %d rows " m)))) |
| 735 | (setq m max-height))) | 740 | (setq m max-height))) |
| 736 | (message "One moment, please...") | 741 | (message "One moment, please...") |
| @@ -762,9 +767,8 @@ Use \\[describe-mode] for more info." | |||
| 762 | (setq score (aref gomoku-score-table square)) | 767 | (setq score (aref gomoku-score-table square)) |
| 763 | (gomoku-play-move square 6) | 768 | (gomoku-play-move square 6) |
| 764 | (cond ((>= score gomoku-winning-threshold) | 769 | (cond ((>= score gomoku-winning-threshold) |
| 765 | (gomoku-find-filled-qtuple square 6) | ||
| 766 | (setq gomoku-emacs-won t) ; for font-lock | 770 | (setq gomoku-emacs-won t) ; for font-lock |
| 767 | (gomoku-cross-winning-qtuple) | 771 | (gomoku-find-filled-qtuple square 6) |
| 768 | (gomoku-terminate-game 'emacs-won)) | 772 | (gomoku-terminate-game 'emacs-won)) |
| 769 | ((zerop score) | 773 | ((zerop score) |
| 770 | (gomoku-terminate-game 'nobody-won)) | 774 | (gomoku-terminate-game 'nobody-won)) |
| @@ -775,11 +779,43 @@ Use \\[describe-mode] for more info." | |||
| 775 | (t | 779 | (t |
| 776 | (gomoku-prompt-for-move))))))))) | 780 | (gomoku-prompt-for-move))))))))) |
| 777 | 781 | ||
| 782 | ;; For small square dimensions this is approximate, since though measured in | ||
| 783 | ;; pixels, event's (X . Y) is a character's top-left corner. | ||
| 778 | (defun gomoku-click (click) | 784 | (defun gomoku-click (click) |
| 785 | "Position at the square where you click." | ||
| 786 | (interactive "e") | ||
| 787 | (and (windowp (posn-window (setq click (event-end click)))) | ||
| 788 | (numberp (posn-point click)) | ||
| 789 | (select-window (posn-window click)) | ||
| 790 | (setq click (posn-col-row click)) | ||
| 791 | (gomoku-goto-xy | ||
| 792 | (min (max (/ (+ (- (car click) | ||
| 793 | gomoku-x-offset | ||
| 794 | 1) | ||
| 795 | (window-hscroll) | ||
| 796 | gomoku-square-width | ||
| 797 | (% gomoku-square-width 2) | ||
| 798 | (/ gomoku-square-width 2)) | ||
| 799 | gomoku-square-width) | ||
| 800 | 1) | ||
| 801 | gomoku-board-width) | ||
| 802 | (min (max (/ (+ (- (cdr click) | ||
| 803 | gomoku-y-offset | ||
| 804 | 1) | ||
| 805 | (let ((inhibit-point-motion-hooks t)) | ||
| 806 | (count-lines 1 (window-start))) | ||
| 807 | gomoku-square-height | ||
| 808 | (% gomoku-square-height 2) | ||
| 809 | (/ gomoku-square-height 2)) | ||
| 810 | gomoku-square-height) | ||
| 811 | 1) | ||
| 812 | gomoku-board-height)))) | ||
| 813 | |||
| 814 | (defun gomoku-mouse-play (click) | ||
| 779 | "Play at the square where you click." | 815 | "Play at the square where you click." |
| 780 | (interactive "e") | 816 | (interactive "e") |
| 781 | (mouse-set-point click) | 817 | (if (gomoku-click click) |
| 782 | (gomoku-human-plays)) | 818 | (gomoku-human-plays))) |
| 783 | 819 | ||
| 784 | (defun gomoku-human-plays () | 820 | (defun gomoku-human-plays () |
| 785 | "Signal to the Gomoku program that you have played. | 821 | "Signal to the Gomoku program that you have played. |
| @@ -807,7 +843,6 @@ If the game is finished, this command requests for another game." | |||
| 807 | ;; detecting wins, it just gives an indication that | 843 | ;; detecting wins, it just gives an indication that |
| 808 | ;; we confirm with GOMOKU-FIND-FILLED-QTUPLE. | 844 | ;; we confirm with GOMOKU-FIND-FILLED-QTUPLE. |
| 809 | (gomoku-find-filled-qtuple square 1)) | 845 | (gomoku-find-filled-qtuple square 1)) |
| 810 | (gomoku-cross-winning-qtuple) | ||
| 811 | (gomoku-terminate-game 'human-won)) | 846 | (gomoku-terminate-game 'human-won)) |
| 812 | (t | 847 | (t |
| 813 | (gomoku-emacs-plays))))))))) | 848 | (gomoku-emacs-plays))))))))) |
| @@ -869,13 +904,12 @@ If the game is finished, this command requests for another game." | |||
| 869 | "Ask for another game, and start it." | 904 | "Ask for another game, and start it." |
| 870 | (if (y-or-n-p "Another game ") | 905 | (if (y-or-n-p "Another game ") |
| 871 | (gomoku gomoku-board-width gomoku-board-height) | 906 | (gomoku gomoku-board-width gomoku-board-height) |
| 872 | (message "Chicken !"))) | 907 | (message "Chicken !"))) |
| 873 | 908 | ||
| 874 | (defun gomoku-offer-a-draw () | 909 | (defun gomoku-offer-a-draw () |
| 875 | "Offer a draw and return T if Human accepted it." | 910 | "Offer a draw and return T if Human accepted it." |
| 876 | (or (y-or-n-p "I offer you a draw. Do you accept it ") | 911 | (or (y-or-n-p "I offer you a draw. Do you accept it ") |
| 877 | (prog1 (setq gomoku-human-refused-draw t) | 912 | (not (setq gomoku-human-refused-draw t)))) |
| 878 | nil))) | ||
| 879 | 913 | ||
| 880 | ;;; | 914 | ;;; |
| 881 | ;;; DISPLAYING THE BOARD. | 915 | ;;; DISPLAYING THE BOARD. |
| @@ -910,30 +944,18 @@ If the game is finished, this command requests for another game." | |||
| 910 | ;; 2 instead of 1 because WINDOW-HEIGHT includes the mode line ! | 944 | ;; 2 instead of 1 because WINDOW-HEIGHT includes the mode line ! |
| 911 | gomoku-square-height))) | 945 | gomoku-square-height))) |
| 912 | 946 | ||
| 913 | (defun gomoku-point-x () | ||
| 914 | "Return the board column where point is, or nil if it is not a board column." | ||
| 915 | (let ((col (- (current-column) gomoku-x-offset))) | ||
| 916 | (if (and (>= col 0) | ||
| 917 | (zerop (% col gomoku-square-width)) | ||
| 918 | (<= (setq col (1+ (/ col gomoku-square-width))) | ||
| 919 | gomoku-board-width)) | ||
| 920 | col))) | ||
| 921 | |||
| 922 | (defun gomoku-point-y () | 947 | (defun gomoku-point-y () |
| 923 | "Return the board row where point is, or nil if it is not a board row." | 948 | "Return the board row where point is." |
| 924 | (let ((row (- (count-lines 1 (point)) gomoku-y-offset 1))) | 949 | (let ((inhibit-point-motion-hooks t)) |
| 925 | (if (and (>= row 0) | 950 | (1+ (/ (- (count-lines 1 (point)) gomoku-y-offset (if (bolp) 0 1)) |
| 926 | (zerop (% row gomoku-square-height)) | 951 | gomoku-square-height)))) |
| 927 | (<= (setq row (1+ (/ row gomoku-square-height))) | ||
| 928 | gomoku-board-height)) | ||
| 929 | row))) | ||
| 930 | 952 | ||
| 931 | (defun gomoku-point-square () | 953 | (defun gomoku-point-square () |
| 932 | "Return the index of the square point is on, or nil if not on the board." | 954 | "Return the index of the square point is on." |
| 933 | (let (x y) | 955 | (let ((inhibit-point-motion-hooks t)) |
| 934 | (and (setq x (gomoku-point-x)) | 956 | (gomoku-xy-to-index (1+ (/ (- (current-column) gomoku-x-offset) |
| 935 | (setq y (gomoku-point-y)) | 957 | gomoku-square-width)) |
| 936 | (gomoku-xy-to-index x y)))) | 958 | (gomoku-point-y)))) |
| 937 | 959 | ||
| 938 | (defun gomoku-goto-square (index) | 960 | (defun gomoku-goto-square (index) |
| 939 | "Move point to square number INDEX." | 961 | "Move point to square number INDEX." |
| @@ -941,56 +963,76 @@ If the game is finished, this command requests for another game." | |||
| 941 | 963 | ||
| 942 | (defun gomoku-goto-xy (x y) | 964 | (defun gomoku-goto-xy (x y) |
| 943 | "Move point to square at X, Y coords." | 965 | "Move point to square at X, Y coords." |
| 944 | (goto-line (+ 1 gomoku-y-offset (* gomoku-square-height (1- y)))) | 966 | (let ((inhibit-point-motion-hooks t)) |
| 967 | (goto-line (+ 1 gomoku-y-offset (* gomoku-square-height (1- y))))) | ||
| 945 | (move-to-column (+ gomoku-x-offset (* gomoku-square-width (1- x))))) | 968 | (move-to-column (+ gomoku-x-offset (* gomoku-square-width (1- x))))) |
| 946 | 969 | ||
| 947 | (defun gomoku-plot-square (square value) | 970 | (defun gomoku-plot-square (square value) |
| 948 | "Draw 'X', 'O' or '.' on SQUARE (depending on VALUE), leave point there." | 971 | "Draw 'X', 'O' or '.' on SQUARE depending on VALUE, leave point there." |
| 949 | (gomoku-goto-square square) | 972 | (or (= value 1) |
| 950 | (gomoku-put-char (cond ((= value 1) ?X) | 973 | (gomoku-goto-square square)) |
| 951 | ((= value 6) ?O) | ||
| 952 | (t ?.))) | ||
| 953 | (sit-for 0)) ; Display NOW | ||
| 954 | |||
| 955 | (defun gomoku-put-char (char) | ||
| 956 | "Draw CHAR on the Gomoku screen." | ||
| 957 | (let ((inhibit-read-only t) | 974 | (let ((inhibit-read-only t) |
| 958 | (inhibit-point-motion-hooks t)) | 975 | (inhibit-point-motion-hooks t)) |
| 959 | (insert-and-inherit char) | 976 | (insert-and-inherit (cond ((= value 1) ?X) |
| 977 | ((= value 6) ?O) | ||
| 978 | (?.))) | ||
| 960 | (and window-system | 979 | (and window-system |
| 961 | (eq char ?.) | 980 | (zerop value) |
| 962 | (put-text-property (1- (point)) (point) 'mouse-face 'highlight)) | 981 | (put-text-property (1- (point)) (point) 'mouse-face 'highlight)) |
| 963 | (delete-char 1) | 982 | (delete-char 1) |
| 964 | (backward-char 1))) | 983 | (backward-char 1)) |
| 984 | (sit-for 0)) ; Display NOW | ||
| 965 | 985 | ||
| 966 | (defun gomoku-init-display (n m) | 986 | (defun gomoku-init-display (n m) |
| 967 | "Display an N by M Gomoku board." | 987 | "Display an N by M Gomoku board." |
| 968 | (buffer-disable-undo (current-buffer)) | 988 | (buffer-disable-undo (current-buffer)) |
| 969 | (let ((inhibit-read-only t) | 989 | (let ((inhibit-read-only t) |
| 970 | (string1 (make-string gomoku-x-offset ? )) | 990 | (point 1) opoint |
| 971 | (string2 (make-string (1- gomoku-square-width) ? )) | 991 | (intangible t) |
| 972 | (point 1) | 992 | (i m) j x) |
| 973 | (i m) j) | 993 | ;; Try to minimize number of chars (because of text properties) |
| 994 | (setq tab-width | ||
| 995 | (if (zerop (% gomoku-x-offset gomoku-square-width)) | ||
| 996 | gomoku-square-width | ||
| 997 | (max (/ (+ (% gomoku-x-offset gomoku-square-width) | ||
| 998 | gomoku-square-width 1) 2) 2))) | ||
| 974 | (erase-buffer) | 999 | (erase-buffer) |
| 975 | ;; We do not use gomoku-plot-square which would be too slow for | ||
| 976 | ;; initializing the display. | ||
| 977 | (newline gomoku-y-offset) | 1000 | (newline gomoku-y-offset) |
| 978 | (while (progn | 1001 | (while (progn |
| 979 | (indent-to gomoku-x-offset) | 1002 | (setq j n |
| 980 | (setq j n) | 1003 | x (- gomoku-x-offset gomoku-square-width)) |
| 981 | (while (progn | 1004 | (while (>= (setq j (1- j)) 0) |
| 982 | (put-text-property point (point) 'category 'gomoku-mode) | 1005 | (insert-char ?\t (/ (- (setq x (+ x gomoku-square-width)) |
| 983 | (put-text-property point (point) 'intangible (point)) | 1006 | (current-column)) |
| 984 | (setq point (point)) | 1007 | tab-width)) |
| 985 | (insert ?.) | 1008 | (insert-char ? (- x (current-column))) |
| 986 | (if window-system | 1009 | (if (setq intangible (not intangible)) |
| 987 | (put-text-property point (point) | 1010 | (put-text-property point (point) 'intangible 2)) |
| 988 | 'mouse-face 'highlight)) | 1011 | (and (zerop j) |
| 989 | (> (setq j (1- j)) 0)) | 1012 | (= i (- m 2)) |
| 990 | (insert string2)) | 1013 | (progn |
| 1014 | (while (>= i 3) | ||
| 1015 | (append-to-buffer (current-buffer) opoint (point)) | ||
| 1016 | (setq i (- i 2))) | ||
| 1017 | (goto-char (point-max)))) | ||
| 1018 | (setq point (point)) | ||
| 1019 | (insert ?.) | ||
| 1020 | (if window-system | ||
| 1021 | (put-text-property point (point) | ||
| 1022 | 'mouse-face 'highlight))) | ||
| 991 | (> (setq i (1- i)) 0)) | 1023 | (> (setq i (1- i)) 0)) |
| 1024 | (if (= i (1- m)) | ||
| 1025 | (setq opoint point)) | ||
| 992 | (insert-char ?\n gomoku-square-height)) | 1026 | (insert-char ?\n gomoku-square-height)) |
| 993 | (gomoku-goto-xy (/ (1+ n) 2) (/ (1+ m) 2))) ; center of the board | 1027 | (or (eq (char-after 1) ?.) |
| 1028 | (put-text-property 1 2 'point-entered | ||
| 1029 | (lambda (x x) (if (bobp) (forward-char))))) | ||
| 1030 | (or intangible | ||
| 1031 | (put-text-property point (point) 'intangible 2)) | ||
| 1032 | (put-text-property point (point) 'point-entered | ||
| 1033 | (lambda (x x) (if (eobp) (backward-char)))) | ||
| 1034 | (put-text-property (point-min) (point) 'category 'gomoku-mode)) | ||
| 1035 | (gomoku-goto-xy (/ (1+ n) 2) (/ (1+ m) 2)) ; center of the board | ||
| 994 | (sit-for 0)) ; Display NOW | 1036 | (sit-for 0)) ; Display NOW |
| 995 | 1037 | ||
| 996 | (defun gomoku-display-statistics () | 1038 | (defun gomoku-display-statistics () |
| @@ -998,16 +1040,12 @@ If the game is finished, this command requests for another game." | |||
| 998 | ;; We store this string in the mode-line-process local variable. | 1040 | ;; We store this string in the mode-line-process local variable. |
| 999 | ;; This is certainly not the cleanest way out ... | 1041 | ;; This is certainly not the cleanest way out ... |
| 1000 | (setq mode-line-process | 1042 | (setq mode-line-process |
| 1001 | (cond | 1043 | (format ": Won %d, lost %d%s" |
| 1002 | ((not (zerop gomoku-number-of-draws)) | 1044 | gomoku-number-of-human-wins |
| 1003 | (format ": Won %d, lost %d, drew %d" | 1045 | gomoku-number-of-emacs-wins |
| 1004 | gomoku-number-of-human-wins | 1046 | (if (zerop gomoku-number-of-draws) |
| 1005 | gomoku-number-of-emacs-wins | 1047 | "" |
| 1006 | gomoku-number-of-draws)) | 1048 | (format ", drew %d" gomoku-number-of-draws)))) |
| 1007 | (t | ||
| 1008 | (format ": Won %d, lost %d" | ||
| 1009 | gomoku-number-of-human-wins | ||
| 1010 | gomoku-number-of-emacs-wins)))) | ||
| 1011 | (force-mode-line-update)) | 1049 | (force-mode-line-update)) |
| 1012 | 1050 | ||
| 1013 | (defun gomoku-switch-to-window () | 1051 | (defun gomoku-switch-to-window () |
| @@ -1015,11 +1053,11 @@ If the game is finished, this command requests for another game." | |||
| 1015 | (interactive) | 1053 | (interactive) |
| 1016 | (let ((buff (get-buffer "*Gomoku*"))) | 1054 | (let ((buff (get-buffer "*Gomoku*"))) |
| 1017 | (if buff ; Buffer exists: | 1055 | (if buff ; Buffer exists: |
| 1018 | (switch-to-buffer buff) ; no problem. | 1056 | (switch-to-buffer buff) ; no problem. |
| 1019 | (if gomoku-game-in-progress | 1057 | (if gomoku-game-in-progress |
| 1020 | (gomoku-crash-game)) ; buffer has been killed or something | 1058 | (gomoku-crash-game)) ; buffer has been killed or something |
| 1021 | (switch-to-buffer "*Gomoku*") ; Anyway, start anew. | 1059 | (switch-to-buffer "*Gomoku*") ; Anyway, start anew. |
| 1022 | (gomoku-mode)))) | 1060 | (gomoku-mode)))) |
| 1023 | 1061 | ||
| 1024 | ;;; | 1062 | ;;; |
| 1025 | ;;; CROSSING WINNING QTUPLES. | 1063 | ;;; CROSSING WINNING QTUPLES. |
| @@ -1030,19 +1068,6 @@ If the game is finished, this command requests for another game." | |||
| 1030 | ;; squares ! It only knows the square where the last move has been played and | 1068 | ;; squares ! It only knows the square where the last move has been played and |
| 1031 | ;; who won. The solution is to scan the board along all four directions. | 1069 | ;; who won. The solution is to scan the board along all four directions. |
| 1032 | 1070 | ||
| 1033 | (defvar gomoku-winning-qtuple-beg nil | ||
| 1034 | "First square of the winning qtuple.") | ||
| 1035 | |||
| 1036 | (defvar gomoku-winning-qtuple-end nil | ||
| 1037 | "Last square of the winning qtuple.") | ||
| 1038 | |||
| 1039 | (defvar gomoku-winning-qtuple-dx nil | ||
| 1040 | "Direction of the winning qtuple (along the X axis).") | ||
| 1041 | |||
| 1042 | (defvar gomoku-winning-qtuple-dy nil | ||
| 1043 | "Direction of the winning qtuple (along the Y axis).") | ||
| 1044 | |||
| 1045 | |||
| 1046 | (defun gomoku-find-filled-qtuple (square value) | 1071 | (defun gomoku-find-filled-qtuple (square value) |
| 1047 | "Return T if SQUARE belongs to a qtuple filled with VALUEs." | 1072 | "Return T if SQUARE belongs to a qtuple filled with VALUEs." |
| 1048 | (or (gomoku-check-filled-qtuple square value 1 0) | 1073 | (or (gomoku-check-filled-qtuple square value 1 0) |
| @@ -1052,32 +1077,20 @@ If the game is finished, this command requests for another game." | |||
| 1052 | 1077 | ||
| 1053 | (defun gomoku-check-filled-qtuple (square value dx dy) | 1078 | (defun gomoku-check-filled-qtuple (square value dx dy) |
| 1054 | "Return T if SQUARE belongs to a qtuple filled with VALUEs along DX, DY." | 1079 | "Return T if SQUARE belongs to a qtuple filled with VALUEs along DX, DY." |
| 1055 | ;; And record it in the WINNING-QTUPLE-... variables. | ||
| 1056 | (let ((a 0) (b 0) | 1080 | (let ((a 0) (b 0) |
| 1057 | (left square) (right square) | 1081 | (left square) (right square) |
| 1058 | (depl (gomoku-xy-to-index dx dy)) | 1082 | (depl (gomoku-xy-to-index dx dy))) |
| 1059 | a+4) | ||
| 1060 | (while (and (> a -4) ; stretch tuple left | 1083 | (while (and (> a -4) ; stretch tuple left |
| 1061 | (= value (aref gomoku-board (setq left (- left depl))))) | 1084 | (= value (aref gomoku-board (setq left (- left depl))))) |
| 1062 | (setq a (1- a))) | 1085 | (setq a (1- a))) |
| 1063 | (setq a+4 (+ a 4)) | 1086 | (while (and (< b (+ a 4)) ; stretch tuple right |
| 1064 | (while (and (< b a+4) ; stretch tuple right | ||
| 1065 | (= value (aref gomoku-board (setq right (+ right depl))))) | 1087 | (= value (aref gomoku-board (setq right (+ right depl))))) |
| 1066 | (setq b (1+ b))) | 1088 | (setq b (1+ b))) |
| 1067 | (cond ((= b a+4) ; tuple length = 5 ? | 1089 | (cond ((= b (+ a 4)) ; tuple length = 5 ? |
| 1068 | (setq gomoku-winning-qtuple-beg (+ square (* a depl)) | 1090 | (gomoku-cross-qtuple (+ square (* a depl)) (+ square (* b depl)) |
| 1069 | gomoku-winning-qtuple-end (+ square (* b depl)) | 1091 | dx dy) |
| 1070 | gomoku-winning-qtuple-dx dx | ||
| 1071 | gomoku-winning-qtuple-dy dy) | ||
| 1072 | t)))) | 1092 | t)))) |
| 1073 | 1093 | ||
| 1074 | (defun gomoku-cross-winning-qtuple () | ||
| 1075 | "Cross winning qtuple, as found by `gomoku-find-filled-qtuple'." | ||
| 1076 | (gomoku-cross-qtuple gomoku-winning-qtuple-beg | ||
| 1077 | gomoku-winning-qtuple-end | ||
| 1078 | gomoku-winning-qtuple-dx | ||
| 1079 | gomoku-winning-qtuple-dy)) | ||
| 1080 | |||
| 1081 | (defun gomoku-cross-qtuple (square1 square2 dx dy) | 1094 | (defun gomoku-cross-qtuple (square1 square2 dx dy) |
| 1082 | "Cross every square between SQUARE1 and SQUARE2 in the DX, DY direction." | 1095 | "Cross every square between SQUARE1 and SQUARE2 in the DX, DY direction." |
| 1083 | (save-excursion ; Not moving point from last square | 1096 | (save-excursion ; Not moving point from last square |
| @@ -1092,7 +1105,9 @@ If the game is finished, this command requests for another game." | |||
| 1092 | ((= dy 0) ; Horizontal | 1105 | ((= dy 0) ; Horizontal |
| 1093 | (forward-char 1) | 1106 | (forward-char 1) |
| 1094 | (insert-char ?- (1- gomoku-square-width) t) | 1107 | (insert-char ?- (1- gomoku-square-width) t) |
| 1095 | (delete-char (1- gomoku-square-width))) | 1108 | (delete-region (point) (progn |
| 1109 | (skip-chars-forward " \t") | ||
| 1110 | (point)))) | ||
| 1096 | ((= dx 0) ; Vertical | 1111 | ((= dx 0) ; Vertical |
| 1097 | (let ((n 1) | 1112 | (let ((n 1) |
| 1098 | (column (current-column))) | 1113 | (column (current-column))) |
| @@ -1102,13 +1117,11 @@ If the game is finished, this command requests for another game." | |||
| 1102 | (indent-to column) | 1117 | (indent-to column) |
| 1103 | (insert-and-inherit ?|)))) | 1118 | (insert-and-inherit ?|)))) |
| 1104 | ((= dx -1) ; 1st Diagonal | 1119 | ((= dx -1) ; 1st Diagonal |
| 1105 | (backward-char (/ gomoku-square-width 2)) | 1120 | (indent-to (prog1 (- (current-column) (/ gomoku-square-width 2)) |
| 1106 | (indent-to (prog1 (current-column) | ||
| 1107 | (forward-line (/ gomoku-square-height 2)))) | 1121 | (forward-line (/ gomoku-square-height 2)))) |
| 1108 | (insert-and-inherit ?/)) | 1122 | (insert-and-inherit ?/)) |
| 1109 | (t ; 2nd Diagonal | 1123 | (t ; 2nd Diagonal |
| 1110 | (forward-char (/ gomoku-square-width 2)) | 1124 | (indent-to (prog1 (+ (current-column) (/ gomoku-square-width 2)) |
| 1111 | (indent-to (prog1 (current-column) | ||
| 1112 | (forward-line (/ gomoku-square-height 2)))) | 1125 | (forward-line (/ gomoku-square-height 2)))) |
| 1113 | (insert-and-inherit ?\\)))))) | 1126 | (insert-and-inherit ?\\)))))) |
| 1114 | (sit-for 0)) ; Display NOW | 1127 | (sit-for 0)) ; Display NOW |
| @@ -1120,18 +1133,14 @@ If the game is finished, this command requests for another game." | |||
| 1120 | (defun gomoku-move-down () | 1133 | (defun gomoku-move-down () |
| 1121 | "Move point down one row on the Gomoku board." | 1134 | "Move point down one row on the Gomoku board." |
| 1122 | (interactive) | 1135 | (interactive) |
| 1123 | (let ((y (gomoku-point-y))) | 1136 | (if (< (gomoku-point-y) gomoku-board-height) |
| 1124 | (next-line (cond ((null y) 1) | 1137 | (next-line gomoku-square-height))) |
| 1125 | ((< y gomoku-board-height) gomoku-square-height) | ||
| 1126 | (t 0))))) | ||
| 1127 | 1138 | ||
| 1128 | (defun gomoku-move-up () | 1139 | (defun gomoku-move-up () |
| 1129 | "Move point up one row on the Gomoku board." | 1140 | "Move point up one row on the Gomoku board." |
| 1130 | (interactive) | 1141 | (interactive) |
| 1131 | (let ((y (gomoku-point-y))) | 1142 | (if (> (gomoku-point-y) 1) |
| 1132 | (previous-line (cond ((null y) 1) | 1143 | (previous-line gomoku-square-height))) |
| 1133 | ((> y 1) gomoku-square-height) | ||
| 1134 | (t 0))))) | ||
| 1135 | 1144 | ||
| 1136 | (defun gomoku-move-ne () | 1145 | (defun gomoku-move-ne () |
| 1137 | "Move point North East on the Gomoku board." | 1146 | "Move point North East on the Gomoku board." |
| @@ -1157,6 +1166,17 @@ If the game is finished, this command requests for another game." | |||
| 1157 | (gomoku-move-down) | 1166 | (gomoku-move-down) |
| 1158 | (backward-char)) | 1167 | (backward-char)) |
| 1159 | 1168 | ||
| 1169 | (defun gomoku-beginning-of-line () | ||
| 1170 | "Move point to first square on the Gomoku board row." | ||
| 1171 | (interactive) | ||
| 1172 | (move-to-column gomoku-x-offset)) | ||
| 1173 | |||
| 1174 | (defun gomoku-end-of-line () | ||
| 1175 | "Move point to last square on the Gomoku board row." | ||
| 1176 | (interactive) | ||
| 1177 | (move-to-column (+ gomoku-x-offset | ||
| 1178 | (* gomoku-square-width (1- gomoku-board-width))))) | ||
| 1179 | |||
| 1160 | (provide 'gomoku) | 1180 | (provide 'gomoku) |
| 1161 | 1181 | ||
| 1162 | ;;; gomoku.el ends here | 1182 | ;;; gomoku.el ends here |