emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/wpuzzle ffae008 1/4: move wpuzzle in packages directory


From: Stefan Monnier
Subject: [elpa] externals/wpuzzle ffae008 1/4: move wpuzzle in packages directory
Date: Sat, 28 Nov 2020 14:41:23 -0500 (EST)

branch: externals/wpuzzle
commit ffae00827bf3023de88f8830203571c123289be0
Author: Ivan Kanis <ivan@kanis.fr>
Commit: Ivan Kanis <ivan@kanis.fr>

    move wpuzzle in packages directory
---
 wpuzzle.el | 438 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 438 insertions(+)

diff --git a/wpuzzle.el b/wpuzzle.el
new file mode 100644
index 0000000..5602c72
--- /dev/null
+++ b/wpuzzle.el
@@ -0,0 +1,438 @@
+;;; wpuzzle.el --- find as many word in a given time  -*- coding: utf-8; 
lexical-binding: t -*-
+
+;; Copyright (C) 2014  Free Software Foundation, Inc.
+
+;; Author: Ivan Kanis <ivan@kanis.fr>
+;; Version: 1.0
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs 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 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs 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 GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Find as many word as possible in a 100 seconds. Words are scored by
+;; length and the scrablle letter value.
+
+;; M-x 100secwp to start the game
+
+;; You need to have aspell installed, it will check for valid words.
+
+;;;; THANKS:
+
+;; Inspiration from an Android game written by SpiceLabs http://spicelabs.in
+
+;; I dedicate this code to my grandmother who taught me to play Scrabble
+
+;;;; BUGS:
+
+;;;; INSTALLATION:
+
+;; Use ELPA
+
+;; install aspell english dictionary. On Ubuntu or Debian type the following:
+
+;; sudo apt-get install aspell aspell-en
+
+;;;; TODO
+
+;;  - add other languages such as french
+;;  - input letter one by one like the original game
+;;  - really stop after 100 seconds
+;;  - display something more fancy with letter points (SVG would be cool!)
+;;  - use ispell.el
+;;  - display best possible score on a given deck at the end of the game
+;;  - use gamegrid.el for dealing with high score
+;;  - use defcustom for variables
+;;  - add unit testing
+;;  - use global state less (functional style programming)
+;;  - clock ticks with timer
+;;  - use face to display picked letter
+;;    (insert (propertize "foo" 'face 'highlight))
+;;  - kill score buffer when quiting
+;;  - use a list instead of a string for the deck letters
+;;  - add command to shuffle the deck
+;;  - navigate to source code in other window to pretend working while playing
+
+;; search for TODO within the file
+
+;;;; VERSION
+
+;; version 1
+
+;;; Code:
+
+(require 'thingatpt)
+
+(defvar 100secwp-time-limit 100
+  "Number of seconds the game will last.")
+
+(defvar 100secwp-high-score-buffer "100secwp-score"
+  "File for holding high scores.")
+
+(defvar 100secwp-high-score-directory
+  (locate-user-emacs-file "games/")
+  "A directory for storing game high score.")
+
+(defvar 100secwp-high-score-file
+  (concat 100secwp-high-score-directory 100secwp-high-score-buffer)
+  "Full path to file used for storing game high score.")
+
+(defvar 100secwp-buffer "*100secwp*"
+  "Game buffer")
+
+(defvar 100secwp-state
+  '((deck-letter)
+    (score)
+    (start-time)
+    (correct-word))
+  "Global game state.")
+
+(defconst 100secwp-frequency
+  '((?e . 111)
+    (?a . 84)
+    (?r . 75)
+    (?i . 75)
+    (?o . 71)
+    (?t . 69)
+    (?n . 66)
+    (?s . 57)
+    (?l . 54)
+    (?c . 45)
+    (?u . 36)
+    (?d . 70) ; crank up for verb ending in ed (normally 33)
+    (?p . 31)
+    (?m . 30)
+    (?h . 30)
+    (?g . 70) ; same for ing (normally 33)
+    (?b . 20)
+    (?f . 18)
+    (?y . 17)
+    (?w . 12)
+    (?k . 11)
+    (?v . 10)
+    (?x . 10) ; (normally 2) remaining letters are cranked up to
+    (?z . 10) ;              add a bit of spice to the game :)
+    (?j . 10) ; (normally 1)
+    (?q . 10))
+  "English letter frequency.")
+
+(defconst 100secwp-scrabble
+  '((?a . 1) (?b . 3) (?c . 3) (?d . 2) (?e . 1) (?f . 4) (?g . 2) (?h . 4)
+    (?i . 1) (?j . 8) (?k . 5) (?l . 1) (?m . 3) (?n . 1) (?o . 1) (?p . 3)
+    (?q . 10) (?r . 1) (?s . 1) (?t . 1) (?u . 1) (?v . 4) (?w . 4) (?x . 8)
+    (?y . 4) (?z . 10))
+  "Scrabble letter values.")
+
+(defmacro 100secwp-state (key)
+  "Return KEY stored variable state."
+  `(cdr (assoc ',key 100secwp-state)))
+
+(defmacro 100secwp-add (place number)
+  "Append number PLACE with CHAR."
+  `(progn (setf ,place (+ ,place ,number))))
+
+(defmacro 100secwp-append (place element)
+  "Append to list PLACE with ELEMENT."
+  `(setf ,place (append ,place (list ,element))))
+
+(defun 100secwp-coerce (x type)
+  "Coerce OBJECT to type TYPE.
+TYPE is a Common Lisp type specifier.
+\n(fn OBJECT TYPE)"
+  (cond ((eq type 'list) (if (listp x) x (append x nil)))
+        ((eq type 'string) (if (stringp x) x (concat x)))
+        (t (error "Can't coerce %s to type %s" x type))))
+
+(defun 100secwp-pick-letter ()
+  "Pick a random letter."
+  (string
+   (let* ((start 0)
+          (sum (let ((ret 0)
+                     (list 100secwp-frequency))
+                 (while list
+                   (setq ret (+ ret (cdr (car list)))
+                         list (cdr list))) ret))
+          (pick (random sum))
+          (ret ?e)
+          (list 100secwp-frequency))
+     (while list
+       (when (< start pick)
+         (setq ret (car (car list))))
+       (setq start (+ start (cdr (car list)))
+             list (cdr list))) ret)))
+
+(defun 100secwp-generate-first-deck ()
+  "Generate first deck of letters."
+  (let ((word (100secwp-generate-first-deck-1)))
+    (while (100secwp-insane-deck word)
+      (setq word (100secwp-generate-first-deck-1))) word))
+
+(defun 100secwp-generate-first-deck-1 ()
+  "Generate a ten letter deck."
+  (let ((word "")
+        (index 0))
+    (while (< index 10)
+      (setq word (concat (100secwp-pick-letter) word)
+            index (1+ index))) word))
+
+(defun 100secwp-generate-next-deck (deck input)
+  "Remove INPUT in DECK and pick a new letter.
+Return new string, nil if INPUT is not in DECK."
+  (let ((match (string-match input deck)))
+    (when match
+      (if (catch 'done
+            (while t
+              (aset deck match (aref (100secwp-pick-letter) 0))
+              (when (not (100secwp-insane-deck deck))
+                (throw 'done t)))) deck))))
+
+(defun 100secwp-set-difference (list1 list2)
+  "Combine LIST1 and LIST2 using a set-difference operation.
+The resulting list contains all items that appear in LIST1 but not LIST2."
+  (if (or (null list1) (null list2)) list1
+    (let ((res nil))
+      (while list1
+        (when (not (member (car list1) list2))
+          (setq res (cons (car list1) res)))
+        (setq list1 (cdr list1))) res)))
+
+(defun 100secwp-insane-deck (word)
+  "Return nil if deck is nice to play with."
+  (let ((vowel-count 0)
+        (index 0)
+        (vowel '(?a ?e ?i ?o ?y))
+        (three-identical-letter nil)
+        (letter-count-alist
+         (let ((character ?a) list)
+           (while (<= character ?z)
+             (setq list (append list (list (cons character 0)))
+                   character (1+ character))) list)))
+    ;; vowel-count vowels and consonant
+    (while (< index (length word))
+      (when (member (aref word index) vowel)
+        (setq vowel-count (1+ vowel-count)))
+      (setq index (1+ index)))
+    (setq vowel-count (or vowel-count 0))
+    ;; count same letter
+    (setq index 0)
+    (while (< index (length word))
+      (when (>= (100secwp-add
+                 (cdr (assoc (aref word index) letter-count-alist)) 1)
+                3)
+        (setq three-identical-letter t))
+      (setq index (1+ index)))
+    (or (< vowel-count 4) (< (- (length word) vowel-count) 3)
+        three-identical-letter)))
+
+(defun 100secwp-sum-word (word)
+  "Return sum of WORD with Scrabble letter value and length."
+  (let ((length (length word))
+        (sum 0)
+        (index 0))
+    (while (< index length)
+      (setq sum (+ sum (cdr (assoc (aref word index) 100secwp-scrabble))))
+      (setq index (1+ index)))
+    (cond ((< length 3)
+           (setq sum 0))
+          ((> length 10)
+           (setq sum (+ sum 100)))
+          (t
+           (setq sum (+ sum
+                        (cdr (assoc length
+                                    '((3 . 5) (4 . 10) (5 . 20) (6 . 40)
+                                      (7 . 50) (8 . 75) (9 . 85))))))))
+
+    sum))
+
+(defun 100secwp-begin-game ()
+  "Reset game state. Display deck."
+  (setf (100secwp-state start-time) (float-time))
+  (setf (100secwp-state score) 0)
+  (setf (100secwp-state deck-letter)
+        (let ((word (100secwp-generate-first-deck-1)))
+          (while (100secwp-insane-deck word)
+            (setq word (100secwp-generate-first-deck-1))) word))
+  (100secwp-generate-first-deck)
+  (setf (100secwp-state correct-word) nil)
+  (100secwp-display-deck nil nil 100secwp-time-limit))
+
+(defun 100secwp-display-deck (invalid-word invalid-input time-left)
+  (erase-buffer)
+  (when (<= time-left 0)
+    (setq time-left 0))
+  (insert (format (concat "%d second"
+                          (if (> time-left 1) "s")
+                          " left        Score %d        High score %d\n")
+                  time-left
+                  (100secwp-state score)
+                  (100secwp-retrieve-high-score)))
+  (let ((deck (100secwp-state deck-letter)))
+    (insert "\n ")
+    (100secwp-display-deck-1 (upcase (substring deck 0 3)))
+    (100secwp-display-deck-1 (upcase (substring deck 3 7)))
+    (insert " ")
+    (100secwp-display-deck-1 (upcase (substring deck 7 10))))
+  (when (stringp invalid-word)
+    (insert (format "\nThe word %s does not exist.\n" invalid-word)))
+
+  (when invalid-input
+    (insert (format "\nThe following letters are not in the deck: %s\n"
+                    (100secwp-coerce invalid-input 'string))))
+  (if (= time-left 0)
+      (progn
+        (100secwp-end-game)
+        (insert "\nThe game is over. Press enter to play one more time.\n\n"))
+    (insert "\nEnter word: ")))
+
+(defun 100secwp-display-deck-1 (letter)
+  (let ((index 0))
+    (while (< index (length letter))
+      (insert (substring letter index (+ 1 index)) " ")
+      (setq index (1+ index)))
+    (insert "\n")))
+
+(defun 100secwp-word-exist (word)
+  "Return t when WORD exists in dictionary."
+  (with-temp-buffer
+    (erase-buffer)
+    (let ((process
+           (start-process
+            "100secwp" (current-buffer)
+            "aspell" "-a" "-B" "--encoding=utf-8")))
+      (process-send-string nil
+                           (concat"%n\n^" word "\n"))
+      (while (accept-process-output process 0.1))
+      (goto-char (point-min))
+      (re-search-forward "^\*$" nil t))))
+
+
+(defun 100secwp-substitute-letter (input)
+  "Pick new letter that are proposed from INPUT."
+  (let ((index 0)
+        (length (length input))
+        exist letter)
+    (while (< index length)
+      (setq letter (substring input index (+ 1 index)))
+      (setq exist (100secwp-generate-next-deck
+                   (100secwp-state deck-letter) letter))
+      (when exist
+        (setf (100secwp-state deck-letter) exist))
+      (setq index (1+ index)))))
+
+
+(defun 100secwp-check-input (input)
+  "Return list of character from INPUT that are not in the deck."
+  (100secwp-set-difference
+   (100secwp-coerce input 'list)
+   (100secwp-coerce (100secwp-state deck-letter) 'list)))
+
+(defun 100secwp-retrieve-high-score ()
+  (when (not (file-exists-p 100secwp-high-score-directory))
+    (make-directory 100secwp-high-score-directory))
+  (save-window-excursion
+    (let ((buffer (find-file 100secwp-high-score-file)) high-score)
+      (goto-char (point-min))
+      (setq high-score
+            (if (word-at-point)
+                (string-to-number (word-at-point))
+              (erase-buffer)
+              (insert "0")
+              (save-buffer) 0))
+      (kill-buffer buffer)
+      high-score)))
+
+(defun 100secwp-end-game ()
+  (let ((max-length 1)
+        (score (100secwp-state score)))
+    (when (not (= (100secwp-state score) 0))
+      (insert "\n\n")
+      (dolist (word (100secwp-state correct-word))
+        (when (> (length word) max-length)
+          (setq max-length (length word))))
+      (dolist (word (100secwp-state correct-word))
+        (insert (format (concat "%-" (int-to-string max-length) "s %d\n")
+                        word (100secwp-sum-word word))))
+      (insert (make-string (+ 4 max-length) ?-) "\n")
+      (insert "sum " (make-string (- max-length 3) ? )
+              (int-to-string score) "\n")
+      (when (> (100secwp-state score) (100secwp-retrieve-high-score))
+        (insert "\nCongratulation, you beat the high score!\n")
+        (save-window-excursion
+          ;; TODO there is duplication with 100secwp-retrieve-high-score
+          ;; maybe it could be refactored in one function setter and getter
+          (let ((buffer (find-file 100secwp-high-score-file)))
+            (erase-buffer)
+            (insert (int-to-string score))
+            (save-buffer)
+            (kill-buffer buffer)))))))
+
+(defun 100secwp-read-input ()
+  "Read input from player."
+  (interactive)
+  (let ((input (word-at-point))
+        (time-left (- 100secwp-time-limit
+                      (- (float-time) (100secwp-state start-time))))
+        (invalid-word nil)
+        (invalid-input nil))
+    (when (< time-left 0)
+      (setq time-left 0))
+    (when input
+      (setq invalid-input (100secwp-check-input input))
+      (when (not invalid-input)
+        (if (100secwp-word-exist input)
+            (progn
+              (100secwp-add (100secwp-state score)
+                            (100secwp-sum-word input))
+              ;; Update global list of correct word to be
+              ;; displayed at the end of the game."
+              (100secwp-append (100secwp-state correct-word) input)
+              (100secwp-substitute-letter input) t)
+          (setq invalid-word input))))
+    (if (and (not input) (= time-left 0))
+        (100secwp-begin-game)
+      (100secwp-display-deck invalid-word invalid-input time-left))))
+
+(define-derived-mode 100secwp-mode text-mode "100secwp"
+  "Major mode for the word by word game."
+  (100secwp-begin-game)
+  (use-local-map
+   (let ((map (make-sparse-keymap)))
+     (define-key map (kbd "RET") '100secwp-read-input) map))
+  (100secwp-begin-game))
+
+;;;###autoload
+(defun 100secwp ()
+  "Start game."
+  (interactive)
+  (switch-to-buffer 100secwp-buffer)
+  (erase-buffer)
+  (switch-to-buffer 100secwp-buffer)
+  (erase-buffer)
+  (insert (format "Welcome to %d seconds word puzzle!
+
+You have %d seconds to type as many word made out of the
+letters presented. Longer words are worth more points. The letters
+are scored with Scrabble value.
+
+Press any key to start." 100secwp-time-limit 100secwp-time-limit))
+  (while (not (aref (read-key-sequence nil) 0))
+    (sit-for 1))
+  (100secwp-mode))
+
+(provide '100secwp)
+
+;; Local Variables:
+;; compile-command: "make"
+;; End:



reply via email to

[Prev in Thread] Current Thread [Next in Thread]