;;; File: mastermind.el ;;; Author: Anders Holst (aho@sans.kth.se) ;;; Copyright (C) Anders Holst 1992 ;;; ;;; LCD Archive Entry: ;;; mastermind|Anders Holst|aho@thalamus.sans.kth.se| ;;; The mastermind game.| ;;; 16-Mar-1993||~/games/mastermind.el.Z| ;;; ;;; This program is free software; you can redistribute it and/or modify ;;; it under the terms of the GNU General Public License as published by ;;; the Free Software Foundation; either version 1, or (at your option) ;;; any later version. ;;; ;;; This program is distributed in the hope that it will be useful, ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;;; GNU General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with your copy of Emacs; if not, write to the Free Software ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ;;; ;;; -------------------------------------------------------------------------- ;; ;; DESCRIPTION ;; ;; This is a simple mastermind game in Emacs. ;; Start with "M-x mastermind". A numeric argument determines how ;; many positions to guess (default is 4). The variable ;; `mastermind-max-color' desides the number of possible "colors", or ;; numbers (defaulted to 6). Maximum of allowed numbers is 10, where ;; "0" is considered as the tenth alternative. ;; The answer for each guessed line is presented as the number of ;; completely correct positions, followed by the number of correct ;; numbers at wrong positions. ;; (defvar mastermind-mode-map nil "Local keymap for mastermind mode") (defvar mastermind-max-color 6 "*The number of different possible \"colors\", i.e. numbers used (max 9).") (if mastermind-mode-map () (setq mastermind-mode-map (make-keymap)) (suppress-keymap mastermind-mode-map t) (define-key mastermind-mode-map "\C-f" 'mm-right) (define-key mastermind-mode-map "\C-b" 'mm-left) (define-key mastermind-mode-map "\C-p" 'mm-up) (define-key mastermind-mode-map "\C-n" 'mm-down) (define-key mastermind-mode-map "\C-e" 'mm-eol) (define-key mastermind-mode-map "\C-a" 'mm-bol) (define-key mastermind-mode-map "\e<" 'mm-top) (define-key mastermind-mode-map "\e>" 'mm-bot) (define-key mastermind-mode-map " " 'mm-space) (define-key mastermind-mode-map "\177" 'mm-bsp) (define-key mastermind-mode-map "0" 'mm-set-number) (define-key mastermind-mode-map "1" 'mm-set-number) (define-key mastermind-mode-map "2" 'mm-set-number) (define-key mastermind-mode-map "3" 'mm-set-number) (define-key mastermind-mode-map "4" 'mm-set-number) (define-key mastermind-mode-map "5" 'mm-set-number) (define-key mastermind-mode-map "6" 'mm-set-number) (define-key mastermind-mode-map "7" 'mm-set-number) (define-key mastermind-mode-map "8" 'mm-set-number) (define-key mastermind-mode-map "9" 'mm-set-number) (define-key mastermind-mode-map "\C-m" 'mm-make-guess)) ;; Mastermind mode is suitable only for specially formatted data. (put 'mastermind-mode 'mode-class 'special) (defun mastermind-mode () "Major mode for playing mastermind. Number -- set number in the position in a line SPC -- blank the number in the position DEL -- blank the number in the previous position RET -- make a guess on the line The answer will be presented as the number of correctly placed numbers followed by the number of correct numbers incorrectly placed. Precisely,\\{mastermind-mode-map}" (interactive) (kill-all-local-variables) (use-local-map mastermind-mode-map) (setq truncate-lines t) (setq major-mode 'mastermind-mode) (setq mode-name "Mastermind")) (defun mastermind (num) "Play mastermind. NUM is number of positions (default 4)." (interactive "P") (switch-to-buffer "*Mastermind*") (mastermind-mode) (setq buffer-read-only t) (buffer-flush-undo (current-buffer)) (setq mm-len (or num 4)) (setq mm-answer (mm-init-line mm-len)) (setq mm-guess ()) (setq mm-check (make-list mm-len ())) (setq mm-guesses 0) (setq mm-pos 0) (setq mm-done ()) (mm-clear-buffer) (mm-blank-line) (mm-new-line)) (defun mm-abs (num) (if (>= num 0) num (- num))) (defun mm-init-line (num) (random t) (let ((line ())) (while (>= (setq num (1- num)) 0) (setq line (cons (1+ (mod (mm-abs (random)) mastermind-max-color)) line))) line)) (defun mm-clear-buffer () (let ((buffer-read-only nil)) (erase-buffer))) (defun mm-new-line () (let (i (buffer-read-only nil)) (insert "\n\n ") (setq i mm-len) (while (>= (setq i (1- i)) 0) (insert "- ")) (setq mm-guess (make-list mm-len ())) (mm-goto 0))) (defun mm-blank-line () (let ((buffer-read-only nil)) (insert (make-string (+ (* 2 mm-len) 3) 32)))) (defun mm-goto (pos) (setq mm-pos (max 0 (min mm-len pos))) (goto-char (point-max)) (beginning-of-line) (forward-char (+ (* mm-pos 2) 3))) (defun mm-in-pos () (= (+ (point-max) (* mm-pos 2) (- (* mm-len 2))) (point))) (defun mm-right () "Move point to next position on line." (interactive) (if (= mm-pos mm-len) () (forward-char 2) (setq mm-pos (1+ mm-pos)))) (defun mm-left () "Move point to previous position on line." (interactive) (if (= mm-pos 0) () (backward-char 2) (setq mm-pos (1- mm-pos)))) (defun mm-up () "Move point up one line." (interactive) (previous-line 2)) (defun mm-down () "Move point down one line." (interactive) (next-line 2)) (defun mm-eol () "Move point to end of the line with the current guess." (interactive) (mm-goto mm-len)) (defun mm-bol () "Move point to beginning of the line with the current guess." (interactive) (mm-goto 0)) (defun mm-top () "Move to the first line in buffer" (interactive) (goto-char (+ (* mm-pos 2) 4))) (defun mm-bot () "Move to the line with the current guess" (interactive) (mm-goto mm-pos)) (defun mm-set-pos (num) (let ((buffer-read-only nil)) (mm-goto mm-pos) (cond (mm-done (message "Game over! Do `M-x mastermind' to start a new game") (ding)) ((= mm-pos mm-len) ()) ((null num) (setcar (nthcdr mm-pos mm-guess) ()) (delete-char 1) (insert-char 45 1) (backward-char 1)) (t (setcar (nthcdr mm-pos mm-guess) num) (delete-char 1) (insert-char (+ 48 num) 1) (backward-char 1))))) (defun mm-space () "Blank the guess at the current position and move to the next position" (interactive) (mm-set-pos ()) (mm-right)) (defun mm-bsp () "Move to the previous position and blank the guess there" (interactive) (if (not (= mm-pos 0)) (progn (mm-left) (mm-set-pos ())))) (defun mm-set-number () "Set the invoking number in the current position, and move right" (interactive) (let ((num (- (string-to-char (this-command-keys)) ?0))) (if (and (<= num mastermind-max-color) (or (= 10 mastermind-max-color) (> num 0))) (progn (mm-set-pos num) (mm-right)) (progn (message (format "%d is not an allowed number. Max is %d" num mastermind-max-color)) (ding))))) (defun mm-nil-pos () (let ((tmp (memq () mm-guess))) (if tmp (- mm-len (length tmp))))) (defun mm-make-guess () "Make a guess on the last written line. The number of correctly placed numbers, followed by the number of incorrectly placed numbers are written after the line." (interactive) (let (tmp (buffer-read-only ())) (cond (mm-done (message "Game over! Do `M-x mastermind' to start a new game") (ding)) ((setq tmp (mm-nil-pos)) (mm-goto tmp)) ((not (mm-in-pos)) (mm-goto mm-pos)) ((setq tmp (mm-check-guess)) (mm-goto mm-len) (setq mm-guesses (1+ mm-guesses)) (insert (concat " ! " (int-to-string (car tmp)) " " (int-to-string (cdr tmp)))) (if (not (= (car tmp) mm-len)) (mm-new-line) (progn (insert "\n\n") (mm-blank-line) (mm-goto 0) (insert (format "Correct. It took %d guesses." mm-guesses)) (mm-goto 0) (setq mm-done t))))))) (defun mm-check-guess () (let (i j (res1 0) (res2 0)) (setq i mm-len) (while (>= (setq i (1- i)) 0) (setcar (nthcdr i mm-check) ())) (setq i mm-len) (while (>= (setq i (1- i)) 0) (if (= (nth i mm-guess) (nth i mm-answer)) (progn (setq res1 (1+ res1)) (setcar (nthcdr i mm-check) t)))) (setq i mm-len) (while (>= (setq i (1- i)) 0) (if (not (= (nth i mm-guess) (nth i mm-answer))) (progn (setq j mm-len) (while (>= (setq j (1- j)) 0) (if (and (not (nth j mm-check)) (not (= i j)) (= (nth i mm-guess) (nth j mm-answer))) (progn (setq res2 (1+ res2)) (setcar (nthcdr j mm-check) t) (setq j 0))))))) (cons res1 res2))) .