| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 | 
							- ;;; org-sudoku.el --- Greate and solve SUDOKU games in Org tables
 
- ;; Copyright (C) 2011, 2012 Free Software Foundation, Inc.
 
- ;;
 
- ;; Author: Carsten Dominik <carsten at orgmode dot org>
 
- ;; Keywords: outlines, hypermedia, calendar, wp, games
 
- ;; Homepage: http://orgmode.org
 
- ;; Version: 0.01
 
- ;;
 
- ;; This file is not yet 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, 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; see the file COPYING.  If not, write to the
 
- ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 
- ;; Boston, MA 02110-1301, USA.
 
- ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
- ;;
 
- ;;; Commentary:
 
- ;;
 
- ;; This is a quick hack to create and solve SUDOKU games in org tables.
 
- ;;
 
- ;; Commands:
 
- ;;
 
- ;; org-sudoku-create         Create a new SUDOKU game
 
- ;; org-sudoku-solve-field    Solve the field at point in a SUDOKU game
 
- ;;                           (this is for cheeting when you are stuck)
 
- ;; org-sudoku-solve          Solve the entire game
 
- ;;
 
- ;;; Code
 
- (require 'org)
 
- (require 'org-table)
 
- ;;; Customization
 
- (defvar org-sudoku-size 9
 
-   "The size of the sudoku game, 9 for a 9x9 game and 4 for a 4x4 game.
 
- Larger games do not seem to work because of limited resources - even though
 
- the algorithm is general.")
 
- (defvar org-sudoku-timeout 2.0
 
-   "Timeout for finding a solution when creating a new game.
 
- After this timeout, the program starts over from scratch to create
 
- a game.")
 
- ;;; Interactive commands
 
- (defun org-sudoku-create (nfilled)
 
-   "Create a sudoku game."
 
-   (interactive "nNumber of pre-filled fields: ")
 
-   (let ((sizesq org-sudoku-size)
 
- 	game)
 
-     (loop for i from 1 to org-sudoku-size do
 
- 	  (loop for j from 1 to org-sudoku-size do
 
- 		(push (list (cons i j) 0) game)))
 
-     (setq game (nreverse game))
 
-     (random t)
 
-     (setq game (org-sudoku-build-allowed game))
 
-     (setq game (org-sudoku-set-field game (cons 1 1)
 
- 				     (1+ (random org-sudoku-size))))
 
-     (catch 'solved
 
-       (let ((cnt 0))
 
- 	(while t
 
- 	  (catch 'abort
 
- 	    (message "Attempt %d to create a game" (setq cnt (1+ cnt)))
 
- 	    (setq game1 (org-sudoku-deep-copy game))
 
- 	    (setq game1 (org-sudoku-solve-game
 
- 			 game1 'random (+ (float-time) org-sudoku-timeout)))
 
- 	    (when game1
 
- 	      (setq game game1)
 
- 	      (throw 'solved t))))))
 
-     (let ((sqrtsize (floor (sqrt org-sudoku-size))))
 
-       (loop for i from 1 to org-sudoku-size do
 
- 	    (insert "| |\n")
 
- 	    (if (and (= (mod i sqrtsize) 0) (< i org-sudoku-size))
 
- 		(insert "|-\n")))
 
-       (backward-char 5)
 
-       (org-table-align))
 
-     (while (> (length game) nfilled)
 
-       (setq game (delete (nth (1+ (random (length game))) game) game)))
 
-     (mapc (lambda (e)
 
- 	    (org-table-put (caar e) (cdar e) (int-to-string (nth 1 e))))
 
- 	  game)
 
-     (org-table-align)
 
-     (org-table-goto-line 1)
 
-     (org-table-goto-column 1)
 
-     (message "Enjoy!")))
 
- (defun org-sudoku-solve ()
 
-   "Solve the sudoku game in the table at point."
 
-   (interactive)
 
-   (unless (org-at-table-p)
 
-     (error "not at a table"))
 
-   (let (game)
 
-     (setq game (org-sudoku-get-game))
 
-     (setq game (org-sudoku-build-allowed game))
 
-     (setq game (org-sudoku-solve-game game))
 
-     ;; Insert the values
 
-     (mapc (lambda (e)
 
- 	    (org-table-put (caar e) (cdar e) (int-to-string (nth 1 e))))
 
- 	  game)
 
-     (org-table-align)))
 
- (defun org-sudoku-solve-field ()
 
-   "Just solve the field at point.
 
- This works by solving the whole game, then inserting only the single field."
 
-   (interactive)
 
-   (unless (org-at-table-p)
 
-     (error "Not at a table"))
 
-   (org-table-check-inside-data-field)
 
-   (let ((i (org-table-current-dline))
 
- 	(j (org-table-current-column))
 
- 	game)
 
-     (setq game (org-sudoku-get-game))
 
-     (setq game (org-sudoku-build-allowed game))
 
-     (setq game (org-sudoku-solve-game game))
 
-     (if game
 
- 	(progn
 
- 	  (org-table-put i j (number-to-string 
 
- 			      (nth 1 (assoc (cons i j) game)))
 
- 			 'align)
 
- 	  (org-table-goto-line i)
 
- 	  (org-table-goto-column j))
 
-       (error "No solution"))))
 
- ;;; Internal functions
 
- (defun org-sudoku-get-game ()
 
-   "Interpret table at point as sudoku game and read it.
 
- A game structure is returned."
 
-   (let (b e g i j game)
 
-     
 
-     (org-table-goto-line 1)
 
-     (org-table-goto-column 1)
 
-     (setq b (point))
 
-     (org-table-goto-line org-sudoku-size)
 
-     (org-table-goto-column org-sudoku-size)
 
-     (setq e (point))
 
-     (setq g (org-table-copy-region b e))
 
-     (setq i 0 j 0)
 
-     (mapc (lambda (c)
 
- 	    (setq i (1+ i) j 0)
 
- 	    (mapc
 
- 	     (lambda (v)
 
- 	       (setq j (1+ j))
 
- 	       (push (list (cons i j)
 
- 			   (string-to-number v))
 
- 		     game))
 
- 	     c))
 
- 	  g)
 
-     (nreverse game)))
 
- (defun org-sudoku-build-allowed (game)
 
-   (let (i j v numbers)
 
-     (loop for i from 1 to org-sudoku-size do
 
- 	  (push i numbers))
 
-     (setq numbers (nreverse numbers))
 
-     ;; add the lists of allowed values for each entry
 
-     (setq game (mapcar
 
- 		(lambda (e)
 
- 		  (list (car e) (nth 1 e)
 
- 			(if (= (nth 1 e) 0)
 
- 			    (copy-sequence numbers)
 
- 			  nil)))
 
- 		game))
 
-     ;; remove the known values from the list of allowed values
 
-     (mapc
 
-      (lambda (e)
 
-        (setq i (caar e) j (cdar e) v (cadr e))
 
-        (when (> v 0)
 
- 	 ;; We do have a value here
 
- 	 (mapc
 
- 	  (lambda (f)
 
- 	    (setq a (assoc f game))
 
- 	    (setf (nth 2 a) (delete v (nth 2 a))))
 
- 	  (cons (cons i j) (org-sudoku-rel-fields i j)))))
 
-      game)
 
-     game))
 
- (defun org-sudoku-find-next-constrained-field (game)
 
-   (setq game (mapcar (lambda (e) (if (nth 2 e) e nil)) game))
 
-   (setq game (delq nil game))
 
-   (let (va vb la lb)
 
-     (setq game
 
- 	  (sort game (lambda (a b)
 
- 		       (setq va (nth 1 a) vb (nth 1 b)
 
- 			     la (length (nth 2 a)) lb (length (nth 2 b)))
 
- 		       (cond
 
- 			((and (= va 0) (> vb 0)) t)
 
- 			((and (> va 0) (= vb 0)) nil)
 
- 			((not (= (* va vb) 0)) nil)
 
- 			(t (< la lb))))))
 
-     (if (or (not game) (> 0 (nth 1 (car game))))
 
- 	nil
 
-       (caar game))))
 
- (defun org-sudoku-solve-game (game &optional random stop-at)
 
-   "Solve GAME.
 
- If RANDOM is non-nit, select candidates randomly from a fields option.
 
- If RANDOM is nil, always start with the first allowed value and try
 
- solving from there.
 
- STOP-AT can be a float time, the solver will abort at that time because
 
- it is probably stuck."
 
-   (let (e v v1 allowed next g)
 
-     (when (and stop-at
 
- 	       (> (float-time) stop-at))
 
-       (setq game nil)
 
-       (throw 'abort nil))
 
-     (while (setq next (org-sudoku-find-next-constrained-field game))
 
-       (setq e (assoc next game)
 
- 	    v (nth 1 e)
 
- 	    allowed (nth 2 e))
 
-       (catch 'solved
 
- 	(if (= (length allowed) 1)
 
- 	    (setq game (org-sudoku-set-field game next (car allowed)))
 
- 	  (while allowed
 
- 	    (setq g (org-sudoku-deep-copy game))
 
- 	    (if (not random)
 
- 		(setq v1 (car allowed))
 
- 	      (setq v1 (nth (random (length allowed)) allowed)))
 
- 	    (setq g (org-sudoku-set-field g next v1))
 
- 	    (setq g (org-sudoku-solve-game g random stop-at))
 
- 	    (when g
 
- 	      (setq game g)
 
- 	      (throw 'solved g)))
 
- 	  (setq game nil))))
 
-     (if (or (not game)
 
- 	    (org-sudoku-unknown-field-p game))
 
- 	nil
 
-       game)))
 
- (defun org-sudoku-unknown-field-p (game)
 
-   "Are there still unknown fields in the game?"
 
-   (delq nil (mapcar (lambda (e) (if (> (nth 1 e) 0) nil t)) game)))
 
- (defun org-sudoku-deep-copy (game)
 
-   "Make a copy of the game so that manipulating the copy does not change the parent."
 
-   (mapcar (lambda(e)
 
- 	    (list (car e) (nth 1 e) (copy-sequence (nth 2 e))))
 
- 	  game))
 
- (defun org-sudoku-set-field (game field value)
 
-   "Put VALUE into FIELD, and tell related fields that they cannot be VALUE."
 
-   (let (i j)
 
-     (setq i (car field) j (cdr field))
 
-     (setq a (assoc field game))
 
-     (setf (nth 1 a) value)
 
-     (setf (nth 2 a) nil)
 
-     ;; Remove value from all related fields
 
-     (mapc
 
-      (lambda (f)
 
-        (setq a (assoc f game))
 
-        (setf (nth 2 a) (delete value (nth 2 a))))
 
-      (org-sudoku-rel-fields i j))
 
-     game))
 
- (defun org-sudoku-rel-fields (i j)
 
-   "Compute the list of related fields for field (i j)."
 
-   (let ((sqrtsize (floor (sqrt org-sudoku-size)))
 
- 	ll imin imax jmin jmax f)
 
-     (setq f (cons i j))
 
-     (loop for ii from 1 to org-sudoku-size do
 
- 	  (or (= ii i) (push (cons ii j) ll)))
 
-     (loop for jj from 1 to org-sudoku-size do
 
- 	  (or (= jj j) (push (cons i jj) ll)))
 
-     (setq imin (1+ (* sqrtsize (/ (1- i) sqrtsize)))
 
- 	  imax (+ imin sqrtsize -1))
 
-     (setq jmin (1+ (* sqrtsize (/ (1- j) sqrtsize)))
 
- 	  jmax (+ jmin sqrtsize -1))
 
-     (loop for ii from imin to imax do
 
- 	  (loop for jj from jmin to jmax do
 
- 		(setq ff (cons ii jj))
 
- 		(or (equal ff f)
 
- 		    (member ff ll)
 
- 		    (push ff ll))))
 
-     ll))
 
- ;;; org-sudoku ends here
 
 
  |