New testcase.
[riece] / lisp / riece-hangman.el
1 ;;; riece-hangman.el --- allow channel members to play the hangman game
2 ;; Copyright (C) 1998-2004 Daiki Ueno
3
4 ;; Author: Daiki Ueno <ueno@unixuser.org>
5 ;; Keywords: IRC, riece
6
7 ;; This file is part of Riece.
8
9 ;; This program is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
12 ;; any later version.
13
14 ;; This program is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
21 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 ;; Boston, MA 02111-1307, USA.
23
24 ;;; Commentary:
25
26 ;; NOTE: This is an add-on module for Riece.
27
28 ;;; Code:
29
30 (require 'riece-globals)
31 (require 'riece-identity)
32 (require 'riece-message)
33 (require 'riece-server)
34
35 (defgroup riece-hangman nil
36   "Allow channel members to play the hangman game."
37   :prefix "riece-"
38   :group 'riece)
39
40 (defcustom riece-hangman-hello-regexp "^,hangman$"
41   "Pattern of string to start the game."
42   :type 'string
43   :group 'riece-hangman)
44
45 (defcustom riece-hangman-bye-regexp "^,hangman bye$"
46   "Pattern of string to end the game."
47   :type 'string
48   :group 'riece-hangman)
49
50 (defcustom riece-hangman-words-file "/usr/share/dict/words"
51   "Location of words file."
52   :type 'file
53   :group 'riece-hangman)
54
55 (defvar riece-hangman-player-context-alist nil)
56 (defvar riece-hangman-words-buffer nil)
57
58 (defconst riece-hangman-description
59   "Allow channel members to play the hangman game.")
60
61 (put 'riece-hangman 'riece-addon-default-disabled t)
62
63 (defun riece-hangman-make-context (word)
64   "Make an instance of player context object.
65 This function is for internal use only."
66   (vector word nil 0))
67
68 (defun riece-hangman-context-word (context)
69   "Return the correct word of CONTEXT.
70 This function is for internal use only."
71   (aref context 0))
72
73 (defun riece-hangman-context-guessed (context)
74   "Return the guessed letters in this CONTEXT.
75 This function is for internal use only."
76   (aref context 1))
77
78 (defun riece-hangman-context-missed-count (context)
79   "Return the count of missed guesses in this CONTEXT.
80 This function is for internal use only."
81   (aref context 2))
82
83 (defun riece-hangman-context-set-guessed (context guessed)
84   "Set the GUESSED letters in this CONTEXT.
85 This function is for internal use only."
86   (aset context 1 guessed))
87
88 (defun riece-hangman-context-set-missed-count (context missed-count)
89   "Set the count of MISSED guesses in this CONTEXT.
90 This function is for internal use only."
91   (aset context 2 missed-count))
92
93 (defun riece-hangman-word ()
94   "Return random word.
95 The wordlist is read from `riece-hangman-words-file'."
96   (unless (and riece-hangman-words-buffer
97                (buffer-name riece-hangman-words-buffer))
98     (setq riece-hangman-words-buffer (generate-new-buffer " *riece-hangman*"))
99     (save-excursion
100       (set-buffer riece-hangman-words-buffer)
101       (buffer-disable-undo)
102       (insert-file-contents riece-hangman-words-file)
103       (let ((case-fold-search nil))
104         (delete-non-matching-lines "^[a-z][a-z][a-z][a-z][a-z][a-z]+"))))
105   (save-excursion
106     (set-buffer riece-hangman-words-buffer)
107     (goto-char (1+ (random (buffer-size))))
108     (if (eobp)
109         (beginning-of-line -1)
110       (beginning-of-line))
111     (buffer-substring (point) (progn (end-of-line) (point)))))
112
113 (defun riece-hangman-reply (target string)
114   (riece-display-message
115    (riece-make-message (riece-make-identity riece-real-nickname
116                                             riece-server-name)
117                        (riece-make-identity target riece-server-name)
118                        string 'notice t))
119   (riece-send-string (format "NOTICE %s :%s\r\n" target string)))
120
121 (defun riece-hangman-reply-with-context (user target context)
122   (let ((masked-word (make-string
123                       (length (riece-hangman-context-word context))
124                       ?-))
125         (guessed (copy-sequence (riece-hangman-context-guessed context)))
126         (index 0))
127     (while (< index (length (riece-hangman-context-word context)))
128       (if (memq (aref (riece-hangman-context-word context) index) guessed)
129           (aset masked-word index
130                 (aref (riece-hangman-context-word context) index)))
131       (setq index (1+ index)))
132     (riece-hangman-reply
133      target
134      (format "%s: Word: %s, Guessed: %s"
135              user masked-word
136              (if guessed
137                  (apply #'string (sort guessed #'<))
138                "")))))
139
140 (defun riece-hangman-after-privmsg-hook (prefix string)
141   (if (get 'riece-hangman 'riece-addon-enabled)
142       (let* ((user (riece-prefix-nickname prefix))
143              (parameters (riece-split-parameters string))
144              (targets (split-string (car parameters) ","))
145              (message (nth 1 parameters))
146              case-fold-search
147              pointer word guessed index)
148         (if (string-match riece-hangman-hello-regexp message)
149             (if (riece-identity-assoc user riece-hangman-player-context-alist
150                                       t)
151                 (riece-hangman-reply
152                  (car targets)
153                  (format "%s: You are already playing the game." user))
154               (let ((context (riece-hangman-make-context
155                               (riece-hangman-word))))
156                 (setq riece-hangman-player-context-alist
157                       (cons (cons user context)
158                             riece-hangman-player-context-alist))
159                 (riece-hangman-reply-with-context user (car targets) context)))
160           (if (string-match riece-hangman-bye-regexp message)
161               (when (setq pointer (riece-identity-assoc
162                                    user riece-hangman-player-context-alist t))
163                 (setq riece-hangman-player-context-alist
164                       (delq pointer riece-hangman-player-context-alist))
165                 (riece-hangman-reply
166                  (car targets)
167                  (format "%s: Sorry, the word was \"%s\""
168                          user
169                          (riece-hangman-context-word (cdr pointer)))))
170             (if (setq pointer (riece-identity-assoc
171                                user riece-hangman-player-context-alist t))
172                 (if (or (/= (length message) 1)
173                         (not (string-match "[a-z]" message)))
174                     (riece-hangman-reply
175                      (car targets)
176                      (format "%s: Not a valid guess: %s" user message))
177                   (if (memq (aref message 0)
178                             (riece-hangman-context-guessed (cdr pointer)))
179                       (riece-hangman-reply (car targets)
180                                            (format "%s: Already guessed '%c'"
181                                                    user (aref message 0)))
182                     (setq guessed (riece-hangman-context-set-guessed
183                                    (cdr pointer)
184                                    (cons (aref message 0)
185                                          (riece-hangman-context-guessed
186                                           (cdr pointer))))
187                           word (riece-hangman-context-word (cdr pointer)))
188                     (unless (catch 'found
189                               (setq index 0)
190                               (while (< index (length word))
191                                 (if (eq (aref word index) (aref message 0))
192                                     (throw 'found t))
193                                 (setq index (1+ index))))
194                       (riece-hangman-context-set-missed-count
195                        (cdr pointer)
196                        (1+ (riece-hangman-context-missed-count
197                             (cdr pointer)))))
198                     (if (>= (riece-hangman-context-missed-count (cdr pointer))
199                             7)
200                         (progn
201                           (riece-hangman-reply
202                            (car targets)
203                            (format "%s: Sorry, the word was \"%s\""
204                                    user
205                                    (riece-hangman-context-word (cdr pointer))))
206                           (setq riece-hangman-player-context-alist
207                                 (delq pointer
208                                       riece-hangman-player-context-alist)))
209                       (if (catch 'missing
210                             (setq index 0)
211                             (while (< index (length word))
212                               (unless (memq (aref word index) guessed)
213                                 (throw 'missing t))
214                               (setq index (1+ index))))
215                           (riece-hangman-reply-with-context user (car targets)
216                                                             (cdr pointer))
217                         (riece-hangman-reply (car targets)
218                                              (format "%s: You got it! (%s)"
219                                                      user word))
220                         (setq riece-hangman-player-context-alist
221                               (delq
222                                pointer
223                                riece-hangman-player-context-alist))))))))))))
224
225 (defun riece-hangman-insinuate ()
226   (add-hook 'riece-after-privmsg-hook 'riece-hangman-after-privmsg-hook))
227
228 (defun riece-hangman-uninstall ()
229   (remove-hook 'riece-after-privmsg-hook 'riece-hangman-after-privmsg-hook))
230
231 (defun riece-hangman-enable ()
232   (random t))
233
234 (provide 'riece-hangman)
235
236 ;;; riece-hangman.el ends here