Better seed support for (random).
[bpt/emacs.git] / lisp / play / blackbox.el
CommitLineData
c0274f38
ER
1;;; blackbox.el --- blackbox game in Emacs Lisp
2
acaf905b 3;; Copyright (C) 1985-1987, 1992, 2001-2012 Free Software Foundation, Inc.
9750e079 4
5de9bdab 5;; Author: F. Thomas May <uw-nsr!uw-warp!tom@beaver.cs.washington.edu>
5de9bdab
ER
6;; Adapted-By: ESR
7;; Keywords: games
8
0231f2dc
JB
9;; This file is part of GNU Emacs.
10
b1fc2b50 11;; GNU Emacs is free software: you can redistribute it and/or modify
0231f2dc 12;; it under the terms of the GNU General Public License as published by
b1fc2b50
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
0231f2dc
JB
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
b1fc2b50 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
0231f2dc 23
5de9bdab
ER
24;;; Commentary:
25
b578f267
EN
26;; by F. Thomas May <uw-nsr!uw-warp!tom@beaver.cs.washington.edu>
27;; doc comment by Root Boy Jim <rbj@dsys.icst.nbs.gov>, 27 Apr 89
28;; interface improvements by ESR, Dec 5 1991.
0231f2dc 29
b578f267
EN
30;; The object of the game is to find four hidden balls by shooting rays
31;; into the black box. There are four possibilities: 1) the ray will
32;; pass thru the box undisturbed, 2) it will hit a ball and be absorbed,
33;; 3) it will be deflected and exit the box, or 4) be deflected immediately,
34;; not even being allowed entry into the box.
a1506d29 35;;
b578f267
EN
36;; The strange part is the method of deflection. It seems that rays will
37;; not pass next to a ball, and change direction at right angles to avoid it.
a1506d29
JB
38;;
39;; R 3
40;; 1 - - - - - - - - 1
41;; - - - - - - - -
42;; - O - - - - - - 3
43;; 2 - - - - O - O -
44;; 4 - - - - - - - -
45;; 5 - - - - - - - - 5
46;; - - - - - - - - R
47;; H - - - - - - - O
48;; 2 H 4 H
49;;
b578f267 50;; Rays which enter and exit are numbered. You can see that rays 1 & 5 pass
47b5f6d6 51;; thru the box undisturbed. Ray 2 is deflected by the northwesternmost
b578f267
EN
52;; ball. Likewise rays 3 and 4. Rays which hit balls and are absorbed are
53;; marked with H. The bottom of the left and the right of the bottom hit
54;; the southeastern ball directly. Rays may also hit balls after being
47b5f6d6 55;; reflected. Consider the H on the bottom next to the 4. It bounces off
b578f267
EN
56;; the NW-ern most ball and hits the central ball. A ray shot from above
57;; the right side 5 would hit the SE-ern most ball. The R beneath the 5
58;; is because the ball is returned instantly. It is not allowed into
59;; the box if it would reflect immediately. The R on the top is a more
60;; leisurely return. Both central balls would tend to deflect it east
61;; or west, but it cannot go either way, so it just retreats.
62;;
63;; At the end of the game, if you've placed guesses for as many balls as
64;; there are in the box, the true board position will be revealed. Each
65;; `x' is an incorrect guess of yours; `o' is the true location of a ball.
0231f2dc 66
5de9bdab
ER
67;;; Code:
68
49b83be9
PJ
69(defvar bb-board nil
70 "Blackbox board.")
71
72(defvar bb-x -1
73 "Current x-position.")
74
75(defvar bb-y -1
76 "Current y-position.")
77
78(defvar bb-score 0
79 "Current score.")
80
81(defvar bb-detour-count 0
82 "Number of detours.")
83
84(defvar bb-balls-placed nil
85 "List of already placed balls.")
86
35b1f2e9
EZ
87;; This is used below to remap existing bindings for cursor motion to
88;; blackbox-specific bindings in blackbox-mode-map. This is so that
89;; users who prefer non-default key bindings for cursor motion don't
90;; lose that when they play Blackbox.
47b5f6d6 91(defun blackbox-redefine-key (map oldfun newfun)
35b1f2e9 92 "Redefine keys that run the function OLDFUN to run NEWFUN instead."
47b5f6d6
SM
93 (define-key map (vector 'remap oldfun) newfun))
94
95
0e23ef9d 96(defvar blackbox-mode-map
47b5f6d6
SM
97 (let ((map (make-keymap)))
98 (suppress-keymap map t)
99 (blackbox-redefine-key map 'backward-char 'bb-left)
100 (blackbox-redefine-key map 'forward-char 'bb-right)
101 (blackbox-redefine-key map 'previous-line 'bb-up)
102 (blackbox-redefine-key map 'next-line 'bb-down)
103 (blackbox-redefine-key map 'move-end-of-line 'bb-eol)
104 (blackbox-redefine-key map 'move-beginning-of-line 'bb-bol)
105 (define-key map " " 'bb-romp)
c14bb1fb 106 (define-key map "q" 'bury-buffer)
47b5f6d6 107 (define-key map [insert] 'bb-romp)
c14bb1fb 108 (define-key map [return] 'bb-done)
47b5f6d6
SM
109 (blackbox-redefine-key map 'newline 'bb-done)
110 map))
0231f2dc
JB
111
112;; Blackbox mode is suitable only for specially formatted data.
113(put 'blackbox-mode 'mode-class 'special)
114
115(defun blackbox-mode ()
6d8b258a
PJ
116 "Major mode for playing blackbox.
117To learn how to play blackbox, see the documentation for function `blackbox'.
0231f2dc
JB
118
119The usual mnemonic keys move the cursor around the box.
120\\<blackbox-mode-map>\\[bb-bol] and \\[bb-eol] move to the beginning and end of line, respectively.
121
122\\[bb-romp] -- send in a ray from point, or toggle a ball at point
09e99870 123\\[bb-done] -- end game and get score"
0231f2dc
JB
124 (interactive)
125 (kill-all-local-variables)
126 (use-local-map blackbox-mode-map)
127 (setq truncate-lines t)
128 (setq major-mode 'blackbox-mode)
09e99870
LK
129 (setq mode-name "Blackbox")
130 (run-mode-hooks 'blackbox-mode-hook))
0231f2dc 131
daa37602 132;;;###autoload
0231f2dc 133(defun blackbox (num)
6d8b258a
PJ
134 "Play blackbox.
135Optional prefix argument is the number of balls; the default is 4.
0231f2dc
JB
136
137What is blackbox?
138
139Blackbox is a game of hide and seek played on an 8 by 8 grid (the
140Blackbox). Your opponent (Emacs, in this case) has hidden several
141balls (usually 4) within this box. By shooting rays into the box and
142observing where they emerge it is possible to deduce the positions of
143the hidden balls. The fewer rays you use to find the balls, the lower
144your score.
145
146Overview of play:
147
151bdc83
JB
148\\<blackbox-mode-map>\
149To play blackbox, type \\[blackbox]. An optional prefix argument
150specifies the number of balls to be hidden in the box; the default is
151four.
0231f2dc
JB
152
153The cursor can be moved around the box with the standard cursor
154movement keys.
155
156To shoot a ray, move the cursor to the edge of the box and press SPC.
157The result will be determined and the playfield updated.
158
159You may place or remove balls in the box by moving the cursor into the
151bdc83 160box and pressing \\[bb-romp].
0231f2dc
JB
161
162When you think the configuration of balls you have placed is correct,
151bdc83
JB
163press \\[bb-done]. You will be informed whether you are correct or
164not, and be given your score. Your score is the number of letters and
165numbers around the outside of the box plus five for each incorrectly
166placed ball. If you placed any balls incorrectly, they will be
167indicated with `x', and their actual positions indicated with `o'.
0231f2dc
JB
168
169Details:
170
171There are three possible outcomes for each ray you send into the box:
172
173 Detour: the ray is deflected and emerges somewhere other than
174 where you sent it in. On the playfield, detours are
175 denoted by matching pairs of numbers -- one where the
176 ray went in, and the other where it came out.
177
178 Reflection: the ray is reflected and emerges in the same place
179 it was sent in. On the playfield, reflections are
180 denoted by the letter `R'.
181
182 Hit: the ray strikes a ball directly and is absorbed. It does
183 not emerge from the box. On the playfield, hits are
184 denoted by the letter `H'.
185
186The rules for how balls deflect rays are simple and are best shown by
187example.
188
189As a ray approaches a ball it is deflected ninety degrees. Rays can
190be deflected multiple times. In the diagrams below, the dashes
191represent empty box locations and the letter `O' represents a ball.
192The entrance and exit points of each ray are marked with numbers as
193described under \"Detour\" above. Note that the entrance and exit
194points are always interchangeable. `*' denotes the path taken by the
195ray.
196
197Note carefully the relative positions of the ball and the ninety
198degree deflection it causes.
199
a1506d29
JB
200 1
201 - * - - - - - - - - - - - - - - - - - - - - - -
202 - * - - - - - - - - - - - - - - - - - - - - - -
2031 * * - - - - - - - - - - - - - - - O - - - - O -
0231f2dc
JB
204 - - O - - - - - - - O - - - - - - - * * * * - -
205 - - - - - - - - - - - * * * * * 2 3 * * * - - * - -
a1506d29
JB
206 - - - - - - - - - - - * - - - - - - - O - * - -
207 - - - - - - - - - - - * - - - - - - - - * * - -
208 - - - - - - - - - - - * - - - - - - - - * - O -
0231f2dc
JB
209 2 3
210
211As mentioned above, a reflection occurs when a ray emerges from the same point
212it was sent in. This can happen in several ways:
213
a1506d29 214
0231f2dc
JB
215 - - - - - - - - - - - - - - - - - - - - - - - -
216 - - - - O - - - - - O - O - - - - - - - - - - -
217R * * * * - - - - - - - * - - - - O - - - - - - -
218 - - - - O - - - - - - * - - - - R - - - - - - - -
219 - - - - - - - - - - - * - - - - - - - - - - - -
220 - - - - - - - - - - - * - - - - - - - - - - - -
221 - - - - - - - - R * * * * - - - - - - - - - - - -
222 - - - - - - - - - - - - O - - - - - - - - - - -
223
224In the first example, the ray is deflected downwards by the upper
225ball, then left by the lower ball, and finally retraces its path to
226its point of origin. The second example is similar. The third
227example is a bit anomalous but can be rationalized by realizing the
228ray never gets a chance to get into the box. Alternatively, the ray
229can be thought of as being deflected downwards and immediately
230emerging from the box.
231
232A hit occurs when a ray runs straight into a ball:
233
234 - - - - - - - - - - - - - - - - - - - - - - - -
235 - - - - - - - - - - - - - - - - - - - - O - - -
236 - - - - - - - - - - - - O - - - H * * * * - - - -
237 - - - - - - - - H * * * * O - - - - - - * - - - -
238 - - - - - - - - - - - - O - - - - - - O - - - -
239H * * * O - - - - - - - - - - - - - - - - - - - -
240 - - - - - - - - - - - - - - - - - - - - - - - -
241 - - - - - - - - - - - - - - - - - - - - - - - -
242
243Be sure to compare the second example of a hit with the first example of
244a reflection."
245 (interactive "P")
246 (switch-to-buffer "*Blackbox*")
247 (blackbox-mode)
248 (setq buffer-read-only t)
249 (buffer-disable-undo (current-buffer))
250 (setq bb-board (bb-init-board (or num 4)))
251 (setq bb-balls-placed nil)
252 (setq bb-x -1)
253 (setq bb-y -1)
254 (setq bb-score 0)
255 (setq bb-detour-count 0)
256 (bb-insert-board)
257 (bb-goto (cons bb-x bb-y)))
258
259(defun bb-init-board (num-balls)
0231f2dc
JB
260 (let (board pos)
261 (while (>= (setq num-balls (1- num-balls)) 0)
262 (while
263 (progn
264 (setq pos (cons (random 8) (random 8)))
49b83be9 265 (member pos board)))
0231f2dc
JB
266 (setq board (cons pos board)))
267 board))
268
269(defun bb-insert-board ()
270 (let (i (buffer-read-only nil))
271 (erase-buffer)
272 (insert " \n")
273 (setq i 8)
274 (while (>= (setq i (1- i)) 0)
275 (insert " - - - - - - - - \n"))
276 (insert " \n")
277 (insert (format "\nThere are %d balls in the box" (length bb-board)))
278 ))
279
4bd2c013
RS
280(defun bb-right (count)
281 (interactive "p")
282 (while (and (> count 0) (< bb-x 8))
0231f2dc 283 (forward-char 2)
4bd2c013
RS
284 (setq bb-x (1+ bb-x))
285 (setq count (1- count))))
0231f2dc 286
4bd2c013
RS
287(defun bb-left (count)
288 (interactive "p")
289 (while (and (> count 0) (> bb-x -1))
0231f2dc 290 (backward-char 2)
4bd2c013
RS
291 (setq bb-x (1- bb-x))
292 (setq count (1- count))))
0231f2dc 293
4bd2c013
RS
294(defun bb-up (count)
295 (interactive "p")
296 (while (and (> count 0) (> bb-y -1))
395bbcd3 297 (with-no-warnings (previous-line))
4bd2c013
RS
298 (setq bb-y (1- bb-y))
299 (setq count (1- count))))
0231f2dc 300
4bd2c013
RS
301(defun bb-down (count)
302 (interactive "p")
303 (while (and (> count 0) (< bb-y 8))
395bbcd3 304 (with-no-warnings (next-line))
4bd2c013
RS
305 (setq bb-y (1+ bb-y))
306 (setq count (1- count))))
0231f2dc
JB
307
308(defun bb-eol ()
309 (interactive)
310 (setq bb-x 8)
311 (bb-goto (cons bb-x bb-y)))
312
313(defun bb-bol ()
314 (interactive)
315 (setq bb-x -1)
316 (bb-goto (cons bb-x bb-y)))
317
318(defun bb-romp ()
319 (interactive)
320 (cond
321 ((and
322 (or (= bb-x -1) (= bb-x 8))
323 (or (= bb-y -1) (= bb-y 8))))
324 ((bb-outside-box bb-x bb-y)
325 (bb-trace-ray bb-x bb-y))
326 (t
327 (bb-place-ball bb-x bb-y))))
328
329(defun bb-place-ball (x y)
330 (let ((coord (cons x y)))
331 (cond
49b83be9
PJ
332 ((member coord bb-balls-placed)
333 (setq bb-balls-placed (delete coord bb-balls-placed))
0231f2dc
JB
334 (bb-update-board "-"))
335 (t
336 (setq bb-balls-placed (cons coord bb-balls-placed))
49b83be9 337 (bb-update-board (propertize "O" 'help-echo "Placed ball"))))))
0231f2dc
JB
338
339(defun bb-trace-ray (x y)
18a6a207
TTN
340 (when (= (following-char) 32)
341 (let ((result (bb-trace-ray-2
342 t
343 x
344 (cond
345 ((= x -1) 1)
346 ((= x 8) -1)
347 (t 0))
348 y
349 (cond
350 ((= y -1) 1)
351 ((= y 8) -1)
352 (t 0)))))
353 (cond
354 ((eq result 'hit)
355 (bb-update-board (propertize "H" 'help-echo "Hit"))
356 (setq bb-score (1+ bb-score)))
357 ((equal result (cons x y))
358 (bb-update-board (propertize "R" 'help-echo "Reflection"))
359 (setq bb-score (1+ bb-score)))
360 (t
361 (setq bb-detour-count (1+ bb-detour-count))
362 (bb-update-board (propertize (format "%d" bb-detour-count)
363 'help-echo "Detour"))
364 (save-excursion
365 (bb-goto result)
366 (bb-update-board (propertize (format "%d" bb-detour-count)
367 'help-echo "Detour")))
368 (setq bb-score (+ bb-score 2)))))))
0231f2dc
JB
369
370(defun bb-trace-ray-2 (first x dx y dy)
371 (cond
372 ((and (not first)
373 (bb-outside-box x y))
374 (cons x y))
49b83be9 375 ((member (cons (+ x dx) (+ y dy)) bb-board)
0231f2dc 376 'hit)
49b83be9 377 ((member (cons (+ x dx dy) (+ y dy dx)) bb-board)
0231f2dc 378 (bb-trace-ray-2 nil x (- dy) y (- dx)))
49b83be9 379 ((member (cons (+ x dx (- dy)) (+ y dy (- dx))) bb-board)
0231f2dc
JB
380 (bb-trace-ray-2 nil x dy y dx))
381 (t
382 (bb-trace-ray-2 nil (+ x dx) dx (+ y dy) dy))))
383
384(defun bb-done ()
385 "Finish the game and report score."
386 (interactive)
387 (let (bogus-balls)
388 (cond
389 ((not (= (length bb-balls-placed) (length bb-board)))
390 (message "There %s %d hidden ball%s; you have placed %d."
391 (if (= (length bb-board) 1) "is" "are")
392 (length bb-board)
393 (if (= (length bb-board) 1) "" "s")
394 (length bb-balls-placed)))
395 (t
396 (setq bogus-balls (bb-show-bogus-balls bb-balls-placed bb-board))
397 (if (= bogus-balls 0)
398 (message "Right! Your score is %d." bb-score)
399 (message "Oops! You missed %d ball%s. Your score is %d."
400 bogus-balls
401 (if (= bogus-balls 1) "" "s")
402 (+ bb-score (* 5 bogus-balls))))
403 (bb-goto '(-1 . -1))))))
404
405(defun bb-show-bogus-balls (balls-placed board)
406 (bb-show-bogus-balls-2 balls-placed board "x")
407 (bb-show-bogus-balls-2 board balls-placed "o"))
408
409(defun bb-show-bogus-balls-2 (list-1 list-2 c)
410 (cond
411 ((null list-1)
412 0)
49b83be9 413 ((member (car list-1) list-2)
0231f2dc
JB
414 (bb-show-bogus-balls-2 (cdr list-1) list-2 c))
415 (t
416 (bb-goto (car list-1))
417 (bb-update-board c)
418 (1+ (bb-show-bogus-balls-2 (cdr list-1) list-2 c)))))
419
151bdc83
JB
420(defun bb-outside-box (x y)
421 (or (= x -1) (= x 8) (= y -1) (= y 8)))
0231f2dc
JB
422
423(defun bb-goto (pos)
424 (goto-char (+ (* (car pos) 2) (* (cdr pos) 22) 26)))
425
426(defun bb-update-board (c)
427 (let ((buffer-read-only nil))
428 (backward-char (1- (length c)))
429 (delete-char (length c))
430 (insert c)
431 (backward-char 1)))
a1506d29 432
896546cd
RS
433(provide 'blackbox)
434
c0274f38 435;;; blackbox.el ends here