;; -*- lexical-binding: t -*- (defun contains (letters word) (seq-some (apply-partially #'seq-contains word) letters)) (defun filter-absentees (letters words) (seq-filter (lambda (w) (not (contains letters w))) words)) (defun mask-board (board word) (let ((valid t)) (dotimes (index (length board) valid) (if (or (and (elt board index) (not (eq (elt board index) (elt word index)))) (and (not (elt board index)) (member (elt word index) board))) (setq valid nil))))) (defun filter-shared-letters (words board) (seq-filter (apply-partially 'mask-board board) words)) (defun likely-letter (words known-letters) (let ((counts (make-hash-table)) (best-letter nil) (best-tally 0)) (dolist (word words) (dolist (letter (seq-uniq word)) (if (not (member letter known-letters)) (puthash letter (1+ (gethash letter counts 0)) counts)))) (maphash (lambda (key value) (if (> value best-tally) (setf best-letter key best-tally value))) counts) best-letter)) (defun build-guesser (word-map) (lambda (board wrong-guesses) (let* ((word-length (length board)) (possible-words (filter-absentees wrong-guesses (gethash word-length word-map))) (masked-words (filter-shared-letters possible-words board)) (known-letters (seq-filter 'identity board))) (likely-letter masked-words (append wrong-guesses known-letters))))) (defun build-dictionary (file) (let ((dictionary (make-hash-table))) (when (file-readable-p file) (with-temp-buffer (insert-file-contents file) (goto-char (point-min)) (while (not (eobp)) (let ((line (buffer-substring (line-beginning-position) (line-end-position)))) (push line (gethash (length line) dictionary (list)))) (forward-line)))) dictionary)) (progn (fset 'guess (build-guesser (build-dictionary "/usr/share/dict/words"))) 'ready)