diff options
| -rw-r--r-- | lisp/ChangeLog | 16 | ||||
| -rw-r--r-- | lisp/play/5x5.el | 391 |
2 files changed, 388 insertions, 19 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 95cae40aebe..7844240186e 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,19 @@ | |||
| 1 | 2011-05-23 Vincent Belaïche <vincentb1@users.sourceforge.net> | ||
| 2 | |||
| 3 | * play/5x5.el: I/ Add an arithmetic solver to suggest positions to | ||
| 4 | click on. II/ Make 5x5 multisession. III/ Ensure that random grids | ||
| 5 | always have a solution in grid size = 5 cases. | ||
| 6 | (5x5-mode-map): Add keybinding to function `5x5-solve-suggest'. | ||
| 7 | (5x5-solver-output, 5x5-log-buffer): New vars. | ||
| 8 | (5x5-grid, 5x5-x-pos, 5x5-y-pos, 5x5-moves, 5x5-cracking): | ||
| 9 | Make these variables buffer local to achieve 5x5 multi-session-ness. | ||
| 10 | (5x5): Set 5x5-grid-size only if SIZE is non-negative. | ||
| 11 | (5x5-grid-to-vec, 5x5-vec-to-grid, 5x5-log-init, 5x5-log, 5x5-solver) | ||
| 12 | (5x5-solve-suggest): New funs. | ||
| 13 | (5x5-randomize): Use 5x5-make-move instead of 5x5-flip-cell to | ||
| 14 | randomize a grid so that we ensure that there is always a solution. | ||
| 15 | (5x5-make-random-grid): Allow other movement than flipping. | ||
| 16 | |||
| 1 | 2011-05-23 Kevin Ryde <user42@zip.com.au> | 17 | 2011-05-23 Kevin Ryde <user42@zip.com.au> |
| 2 | 18 | ||
| 3 | * emacs-lisp/advice.el (ad-read-advised-function): | 19 | * emacs-lisp/advice.el (ad-read-advised-function): |
diff --git a/lisp/play/5x5.el b/lisp/play/5x5.el index 46c3c867304..a5f585d4d86 100644 --- a/lisp/play/5x5.el +++ b/lisp/play/5x5.el | |||
| @@ -1,4 +1,4 @@ | |||
| 1 | ;;; 5x5.el --- simple little puzzle game | 1 | ;;; 5x5.el --- simple little puzzle game -*- coding: utf-8 -*- |
| 2 | 2 | ||
| 3 | ;; Copyright (C) 1999-2011 Free Software Foundation, Inc. | 3 | ;; Copyright (C) 1999-2011 Free Software Foundation, Inc. |
| 4 | 4 | ||
| @@ -24,15 +24,15 @@ | |||
| 24 | 24 | ||
| 25 | ;;; Commentary: | 25 | ;;; Commentary: |
| 26 | 26 | ||
| 27 | ;; The aim of 5x5 is to fill in all the squares. If you need any more of an | 27 | ;; The aim of 5x5 is to fill in all the squares. If you need any more of an |
| 28 | ;; explanation you probably shouldn't play the game. | 28 | ;; explanation you probably shouldn't play the game. |
| 29 | 29 | ||
| 30 | ;;; TODO: | 30 | ;;; TODO: |
| 31 | 31 | ||
| 32 | ;; o The code for updating the grid needs to be re-done. At the moment it | 32 | ;; o The code for updating the grid needs to be re-done. At the moment it |
| 33 | ;; simply re-draws the grid every time a move is made. | 33 | ;; simply re-draws the grid every time a move is made. |
| 34 | ;; | 34 | ;; |
| 35 | ;; o Look into tarting up the display with color. gamegrid.el looks | 35 | ;; o Look into tarting up the display with color. gamegrid.el looks |
| 36 | ;; interesting, perhaps that is the way to go? | 36 | ;; interesting, perhaps that is the way to go? |
| 37 | 37 | ||
| 38 | ;;; Thanks: | 38 | ;;; Thanks: |
| @@ -41,7 +41,10 @@ | |||
| 41 | ;; emacs mode. | 41 | ;; emacs mode. |
| 42 | ;; | 42 | ;; |
| 43 | ;; Pascal Q. Porcupine <joshagam@cs.nmsu.edu> for inspiring the animated | 43 | ;; Pascal Q. Porcupine <joshagam@cs.nmsu.edu> for inspiring the animated |
| 44 | ;; solver. | 44 | ;; cracker. |
| 45 | ;; | ||
| 46 | ;; Vincent Belaïche <vincentb1@users.sourceforge.net> & Jay P. Belanger | ||
| 47 | ;; <jay.p.belanger@gmail.com> for the math solver. | ||
| 45 | 48 | ||
| 46 | ;;; Code: | 49 | ;;; Code: |
| 47 | 50 | ||
| @@ -89,19 +92,25 @@ | |||
| 89 | 92 | ||
| 90 | ;; Non-customize variables. | 93 | ;; Non-customize variables. |
| 91 | 94 | ||
| 92 | (defvar 5x5-grid nil | 95 | (defmacro 5x5-defvar-local (var value doc) |
| 96 | "Define VAR to VALUE with documentation DOC and make it buffer local." | ||
| 97 | `(progn | ||
| 98 | (defvar ,var ,value ,doc) | ||
| 99 | (make-variable-buffer-local (quote ,var)))) | ||
| 100 | |||
| 101 | (5x5-defvar-local 5x5-grid nil | ||
| 93 | "5x5 grid contents.") | 102 | "5x5 grid contents.") |
| 94 | 103 | ||
| 95 | (defvar 5x5-x-pos 2 | 104 | (5x5-defvar-local 5x5-x-pos 2 |
| 96 | "X position of cursor.") | 105 | "X position of cursor.") |
| 97 | 106 | ||
| 98 | (defvar 5x5-y-pos 2 | 107 | (5x5-defvar-local 5x5-y-pos 2 |
| 99 | "Y position of cursor.") | 108 | "Y position of cursor.") |
| 100 | 109 | ||
| 101 | (defvar 5x5-moves 0 | 110 | (5x5-defvar-local 5x5-moves 0 |
| 102 | "Moves made.") | 111 | "Moves made.") |
| 103 | 112 | ||
| 104 | (defvar 5x5-cracking nil | 113 | (5x5-defvar-local 5x5-cracking nil |
| 105 | "Are we in cracking mode?") | 114 | "Are we in cracking mode?") |
| 106 | 115 | ||
| 107 | (defvar 5x5-buffer-name "*5x5*" | 116 | (defvar 5x5-buffer-name "*5x5*" |
| @@ -134,10 +143,28 @@ | |||
| 134 | (define-key map [(control c) (control b)] #'5x5-crack-mutating-best) | 143 | (define-key map [(control c) (control b)] #'5x5-crack-mutating-best) |
| 135 | (define-key map [(control c) (control x)] #'5x5-crack-xor-mutate) | 144 | (define-key map [(control c) (control x)] #'5x5-crack-xor-mutate) |
| 136 | (define-key map "n" #'5x5-new-game) | 145 | (define-key map "n" #'5x5-new-game) |
| 146 | (define-key map "s" #'5x5-solve-suggest) | ||
| 137 | (define-key map "q" #'5x5-quit-game) | 147 | (define-key map "q" #'5x5-quit-game) |
| 138 | map) | 148 | map) |
| 139 | "Local keymap for the 5x5 game.") | 149 | "Local keymap for the 5x5 game.") |
| 140 | 150 | ||
| 151 | (5x5-defvar-local 5x5-solver-output nil | ||
| 152 | "List that is is the output of artihmetic solver. | ||
| 153 | |||
| 154 | This list L is such that | ||
| 155 | |||
| 156 | L = (M S_1 S_2 ... S_N) | ||
| 157 | |||
| 158 | M is the move count when the solve output was stored. | ||
| 159 | |||
| 160 | S_1 ... S_N are all the solutions ordered from least to greatest | ||
| 161 | number of strokes. S_1 is the solution to be displayed. | ||
| 162 | |||
| 163 | Each solution S_1, ..., S_N is a a list (STROKE-COUNT GRID) where | ||
| 164 | STROKE-COUNT is to number of strokes to achieve the solution and | ||
| 165 | GRID is the grid of positions to click.") | ||
| 166 | |||
| 167 | |||
| 141 | ;; Menu definition. | 168 | ;; Menu definition. |
| 142 | 169 | ||
| 143 | (easy-menu-define 5x5-mode-menu 5x5-mode-map "5x5 menu." | 170 | (easy-menu-define 5x5-mode-menu 5x5-mode-map "5x5 menu." |
| @@ -146,6 +173,7 @@ | |||
| 146 | ["Random game" 5x5-randomize t] | 173 | ["Random game" 5x5-randomize t] |
| 147 | ["Quit game" 5x5-quit-game t] | 174 | ["Quit game" 5x5-quit-game t] |
| 148 | "---" | 175 | "---" |
| 176 | ["Use Calc solver" 5x5-solve-suggest t] | ||
| 149 | ["Crack randomly" 5x5-crack-randomly t] | 177 | ["Crack randomly" 5x5-crack-randomly t] |
| 150 | ["Crack mutating current" 5x5-crack-mutating-current t] | 178 | ["Crack mutating current" 5x5-crack-mutating-current t] |
| 151 | ["Crack mutating best" 5x5-crack-mutating-best t] | 179 | ["Crack mutating best" 5x5-crack-mutating-best t] |
| @@ -158,7 +186,7 @@ | |||
| 158 | (defun 5x5-mode () | 186 | (defun 5x5-mode () |
| 159 | "A mode for playing `5x5'. | 187 | "A mode for playing `5x5'. |
| 160 | 188 | ||
| 161 | The key bindings for 5x5-mode are: | 189 | The key bindings for `5x5-mode' are: |
| 162 | 190 | ||
| 163 | \\{5x5-mode-map}" | 191 | \\{5x5-mode-map}" |
| 164 | (kill-all-local-variables) | 192 | (kill-all-local-variables) |
| @@ -194,14 +222,14 @@ Quit current game \\[5x5-quit-game]" | |||
| 194 | 222 | ||
| 195 | (interactive "P") | 223 | (interactive "P") |
| 196 | (setq 5x5-cracking nil) | 224 | (setq 5x5-cracking nil) |
| 197 | (when size | ||
| 198 | (setq 5x5-grid-size size)) | ||
| 199 | (switch-to-buffer 5x5-buffer-name) | 225 | (switch-to-buffer 5x5-buffer-name) |
| 226 | (5x5-mode) | ||
| 227 | (when (natnump size) | ||
| 228 | (setq 5x5-grid-size size)) | ||
| 200 | (if (or (not 5x5-grid) (not (= 5x5-grid-size (length (aref 5x5-grid 0))))) | 229 | (if (or (not 5x5-grid) (not (= 5x5-grid-size (length (aref 5x5-grid 0))))) |
| 201 | (5x5-new-game)) | 230 | (5x5-new-game)) |
| 202 | (5x5-draw-grid (list 5x5-grid)) | 231 | (5x5-draw-grid (list 5x5-grid)) |
| 203 | (5x5-position-cursor) | 232 | (5x5-position-cursor)) |
| 204 | (5x5-mode)) | ||
| 205 | 233 | ||
| 206 | (defun 5x5-new-game () | 234 | (defun 5x5-new-game () |
| 207 | "Start a new game of `5x5'." | 235 | "Start a new game of `5x5'." |
| @@ -277,10 +305,11 @@ Quit current game \\[5x5-quit-game]" | |||
| 277 | 305 | ||
| 278 | (defun 5x5-draw-grid (grids) | 306 | (defun 5x5-draw-grid (grids) |
| 279 | "Draw the grids GRIDS into the current buffer." | 307 | "Draw the grids GRIDS into the current buffer." |
| 280 | (let ((buffer-read-only nil)) | 308 | (let ((inhibit-read-only t) grid-org) |
| 281 | (erase-buffer) | 309 | (erase-buffer) |
| 282 | (loop for grid in grids do (5x5-draw-grid-end)) | 310 | (loop for grid in grids do (5x5-draw-grid-end)) |
| 283 | (insert "\n") | 311 | (insert "\n") |
| 312 | (setq grid-org (point)) | ||
| 284 | (loop for y from 0 to (1- 5x5-grid-size) do | 313 | (loop for y from 0 to (1- 5x5-grid-size) do |
| 285 | (loop for lines from 0 to (1- 5x5-y-scale) do | 314 | (loop for lines from 0 to (1- 5x5-y-scale) do |
| 286 | (loop for grid in grids do | 315 | (loop for grid in grids do |
| @@ -290,6 +319,23 @@ Quit current game \\[5x5-quit-game]" | |||
| 290 | (if (5x5-cell grid y x) ?# ?.)))) | 319 | (if (5x5-cell grid y x) ?# ?.)))) |
| 291 | (insert " | ")) | 320 | (insert " | ")) |
| 292 | (insert "\n"))) | 321 | (insert "\n"))) |
| 322 | (when 5x5-solver-output | ||
| 323 | (if (= (car 5x5-solver-output) 5x5-moves) | ||
| 324 | (save-excursion | ||
| 325 | (goto-char grid-org) | ||
| 326 | (beginning-of-line (+ 1 (/ 5x5-y-scale 2))) | ||
| 327 | (let ((solution-grid (cdadr 5x5-solver-output))) | ||
| 328 | (dotimes (y 5x5-grid-size) | ||
| 329 | (save-excursion | ||
| 330 | (forward-char (+ 1 (/ (1+ 5x5-x-scale) 2))) | ||
| 331 | (dotimes (x 5x5-grid-size) | ||
| 332 | (when (5x5-cell solution-grid y x) | ||
| 333 | (insert-char ?O 1) | ||
| 334 | (delete-char 1) | ||
| 335 | (backward-char)) | ||
| 336 | (forward-char (1+ 5x5-x-scale)))) | ||
| 337 | (forward-line 5x5-y-scale)))) | ||
| 338 | (setq 5x5-solver-output nil))) | ||
| 293 | (loop for grid in grids do (5x5-draw-grid-end)) | 339 | (loop for grid in grids do (5x5-draw-grid-end)) |
| 294 | (insert "\n") | 340 | (insert "\n") |
| 295 | (insert (format "On: %d Moves: %d" (5x5-grid-value (car grids)) 5x5-moves)))) | 341 | (insert (format "On: %d Moves: %d" (5x5-grid-value (car grids)) 5x5-moves)))) |
| @@ -304,13 +350,14 @@ Quit current game \\[5x5-quit-game]" | |||
| 304 | "Keep track of how many moves have been made." | 350 | "Keep track of how many moves have been made." |
| 305 | (incf 5x5-moves)) | 351 | (incf 5x5-moves)) |
| 306 | 352 | ||
| 307 | (defun 5x5-make-random-grid () | 353 | (defun 5x5-make-random-grid (&optional move) |
| 308 | "Make a random grid." | 354 | "Make a random grid." |
| 355 | (setq move (or move (symbol-function '5x5-flip-cell))) | ||
| 309 | (let ((grid (5x5-make-new-grid))) | 356 | (let ((grid (5x5-make-new-grid))) |
| 310 | (loop for y from 0 to (1- 5x5-grid-size) do | 357 | (loop for y from 0 to (1- 5x5-grid-size) do |
| 311 | (loop for x from 0 to (1- 5x5-grid-size) do | 358 | (loop for x from 0 to (1- 5x5-grid-size) do |
| 312 | (if (zerop (random 2)) | 359 | (if (zerop (random 2)) |
| 313 | (5x5-flip-cell grid y x)))) | 360 | (funcall move grid y x)))) |
| 314 | grid)) | 361 | grid)) |
| 315 | 362 | ||
| 316 | ;; Cracker functions. | 363 | ;; Cracker functions. |
| @@ -415,6 +462,312 @@ in progress because it is an animated attempt." | |||
| 415 | (sit-for 5x5-animate-delay)))) | 462 | (sit-for 5x5-animate-delay)))) |
| 416 | 5x5-grid) | 463 | 5x5-grid) |
| 417 | 464 | ||
| 465 | ;; Arithmetic solver | ||
| 466 | ;;=========================================================================== | ||
| 467 | (defun 5x5-grid-to-vec (grid) | ||
| 468 | "Convert GRID to an equivalent Calc matrix of (mod X 2) forms | ||
| 469 | where X is 1 for setting a position, and 0 for unsetting a | ||
| 470 | position." | ||
| 471 | (cons 'vec | ||
| 472 | (mapcar (lambda (y) | ||
| 473 | (cons 'vec | ||
| 474 | (mapcar (lambda (x) | ||
| 475 | (if x '(mod 1 2) '(mod 0 2))) | ||
| 476 | y))) | ||
| 477 | grid))) | ||
| 478 | |||
| 479 | (defun 5x5-vec-to-grid (grid-matrix) | ||
| 480 | "Convert a grid matrix GRID-MATRIX in Calc format to a grid in | ||
| 481 | 5x5 format. See function `5x5-grid-to-vec'." | ||
| 482 | (apply | ||
| 483 | 'vector | ||
| 484 | (mapcar | ||
| 485 | (lambda (x) | ||
| 486 | (apply | ||
| 487 | 'vector | ||
| 488 | (mapcar | ||
| 489 | (lambda (y) (/= (cadr y) 0)) | ||
| 490 | (cdr x)))) | ||
| 491 | (cdr grid-matrix)))) | ||
| 492 | |||
| 493 | (if nil; set to t to enable solver logging | ||
| 494 | (progn | ||
| 495 | (defvar 5x5-log-buffer nil) | ||
| 496 | (defun 5x5-log-init () | ||
| 497 | (if (buffer-live-p 5x5-log-buffer) | ||
| 498 | (with-current-buffer 5x5-log-buffer (erase-buffer)) | ||
| 499 | (setq 5x5-log-buffer (get-buffer-create "*5x5 LOG*")))) | ||
| 500 | |||
| 501 | (defun 5x5-log (name value) | ||
| 502 | "Debug purpuse only. | ||
| 503 | |||
| 504 | Log a matrix VALUE of (mod B 2) forms, only B is output and | ||
| 505 | Scilab matrix notation is used. VALUE is returned so that it is | ||
| 506 | easy to log a value with minimal rewrite of code." | ||
| 507 | (when (buffer-live-p 5x5-log-buffer) | ||
| 508 | (let* ((unpacked-value | ||
| 509 | (math-map-vec | ||
| 510 | (lambda (row) (math-map-vec 'cadr row)) | ||
| 511 | value)) | ||
| 512 | (calc-vector-commas "") | ||
| 513 | (calc-matrix-brackets '(C O)) | ||
| 514 | (value-to-log (math-format-value unpacked-value))) | ||
| 515 | (with-current-buffer 5x5-log-buffer | ||
| 516 | (insert name ?= value-to-log ?\n)))) | ||
| 517 | value)) | ||
| 518 | (defmacro 5x5-log-init ()) | ||
| 519 | (defmacro 5x5-log (name value) value)) | ||
| 520 | |||
| 521 | (defun 5x5-solver (grid) | ||
| 522 | "Return a list of solutions for GRID. | ||
| 523 | |||
| 524 | Given some grid GRID, the returned a list of solution LIST is | ||
| 525 | sorted from least Hamming weight to geatest one. | ||
| 526 | |||
| 527 | LIST = (SOLUTION-1 ... SOLUTION-N) | ||
| 528 | |||
| 529 | Each solution SOLUTION-I is a cons cell (HW . G) where HW is the | ||
| 530 | Hamming weight of the solution --- ie the number of strokes to | ||
| 531 | achieves it --- and G is the grid of positions to click in order | ||
| 532 | to complete the 5x5. | ||
| 533 | |||
| 534 | Solutions are sorted from least to greatest Hamming weight." | ||
| 535 | (require 'calc-ext) | ||
| 536 | (flet ((5x5-mat-mode-2 | ||
| 537 | (a) | ||
| 538 | (math-map-vec | ||
| 539 | (lambda (y) | ||
| 540 | (math-map-vec | ||
| 541 | (lambda (x) `(mod ,x 2)) | ||
| 542 | y)) | ||
| 543 | a))) | ||
| 544 | (let* (calc-command-flags | ||
| 545 | (grid-size-squared (* 5x5-grid-size 5x5-grid-size)) | ||
| 546 | |||
| 547 | ;; targetv is the vector the origine of which is org="current | ||
| 548 | ;; grid" and the end of which is dest="all ones". | ||
| 549 | (targetv | ||
| 550 | (5x5-log | ||
| 551 | "b" | ||
| 552 | (let ( | ||
| 553 | ;; org point is the current grid | ||
| 554 | (org (calcFunc-arrange (5x5-grid-to-vec grid) | ||
| 555 | 1)) | ||
| 556 | |||
| 557 | ;; end point of game is the all ones matrix | ||
| 558 | (dest (calcFunc-cvec '(mod 1 2) grid-size-squared 1))) | ||
| 559 | (math-sub dest org)))) | ||
| 560 | |||
| 561 | ;; transferm is the transfer matrix, ie it is the 25x25 | ||
| 562 | ;; matrix applied everytime a flip is carried out where a | ||
| 563 | ;; flip is defined by a 25x1 Dirac vector --- ie all zeros | ||
| 564 | ;; but 1 in the position that is flipped. | ||
| 565 | (transferm | ||
| 566 | (5x5-log | ||
| 567 | "a" | ||
| 568 | ;; transfer-grid is not a play grid, but this is the | ||
| 569 | ;; transfer matrix in the format of a vector of vectors, we | ||
| 570 | ;; do it this way because random access in vectors is | ||
| 571 | ;; faster. The motivation is just speed as we build it | ||
| 572 | ;; element by element, but that could have been created | ||
| 573 | ;; using only Calc primitives. Probably that would be a | ||
| 574 | ;; better idea to use Calc with some vector manipulation | ||
| 575 | ;; rather than going this way... | ||
| 576 | (5x5-grid-to-vec (let ((transfer-grid | ||
| 577 | (let ((5x5-grid-size grid-size-squared)) | ||
| 578 | (5x5-make-new-grid)))) | ||
| 579 | (dotimes (i 5x5-grid-size) | ||
| 580 | (dotimes (j 5x5-grid-size) | ||
| 581 | ;; k0 = flattened flip position corresponding | ||
| 582 | ;; to (i, j) on the grid. | ||
| 583 | (let* ((k0 (+ (* 5 i) j))) | ||
| 584 | ;; cross center | ||
| 585 | (5x5-set-cell transfer-grid k0 k0 t) | ||
| 586 | ;; Cross top. | ||
| 587 | (and | ||
| 588 | (> i 0) | ||
| 589 | (5x5-set-cell transfer-grid | ||
| 590 | (- k0 5x5-grid-size) k0 t)) | ||
| 591 | ;; Cross bottom. | ||
| 592 | (and | ||
| 593 | (< (1+ i) 5x5-grid-size) | ||
| 594 | (5x5-set-cell transfer-grid | ||
| 595 | (+ k0 5x5-grid-size) k0 t)) | ||
| 596 | ;; Cross left. | ||
| 597 | (and | ||
| 598 | (> j 0) | ||
| 599 | (5x5-set-cell transfer-grid (1- k0) k0 t)) | ||
| 600 | ;; Cross right. | ||
| 601 | (and | ||
| 602 | (< (1+ j) 5x5-grid-size) | ||
| 603 | (5x5-set-cell transfer-grid | ||
| 604 | (1+ k0) k0 t))))) | ||
| 605 | transfer-grid)))) | ||
| 606 | ;; TODO: this is hard-coded for grid-size = 5, make it generic. | ||
| 607 | (transferm-kernel-size | ||
| 608 | (if (= 5x5-grid-size 5) 2 | ||
| 609 | (error "Transfer matrix rank not known for grid-size != 5"))) | ||
| 610 | |||
| 611 | ;; TODO: this is hard-coded for grid-size = 5, make it generic. | ||
| 612 | ;; | ||
| 613 | ;; base-change is a 25x25 matrix, where topleft submatrix | ||
| 614 | ;; 23x25 is a diagonal of 1, and the two last columns are a | ||
| 615 | ;; base of kernel of transferm. | ||
| 616 | ;; | ||
| 617 | ;; base-change must be by construction inversible. | ||
| 618 | (base-change | ||
| 619 | (5x5-log | ||
| 620 | "p" | ||
| 621 | (let ((id (5x5-mat-mode-2 (calcFunc-diag 1 grid-size-squared)))) | ||
| 622 | (setcdr (last id (1+ transferm-kernel-size)) | ||
| 623 | (cdr (5x5-mat-mode-2 | ||
| 624 | '(vec (vec 0 1 1 1 0 1 0 1 0 1 1 1 0 1 | ||
| 625 | 1 1 0 1 0 1 0 1 1 1 0) | ||
| 626 | (vec 1 1 0 1 1 0 0 0 0 0 1 1 0 1 | ||
| 627 | 1 0 0 0 0 0 1 1 0 1 1))))) | ||
| 628 | (calcFunc-trn id)))) | ||
| 629 | |||
| 630 | (inv-base-change | ||
| 631 | (5x5-log "invp" | ||
| 632 | (calcFunc-inv base-change))) | ||
| 633 | |||
| 634 | ;; B:= targetv | ||
| 635 | ;; A:= transferm | ||
| 636 | ;; P:= base-change | ||
| 637 | ;; P^-1 := inv-base-change | ||
| 638 | ;; X := solution | ||
| 639 | |||
| 640 | ;; B = A * X | ||
| 641 | ;; P^-1 * B = P^-1 * A * P * P^-1 * X | ||
| 642 | ;; CX = P^-1 * X | ||
| 643 | ;; CA = P^-1 * A * P | ||
| 644 | ;; CB = P^-1 * B | ||
| 645 | ;; CB = CA * CX | ||
| 646 | ;; CX = CA^-1 * CB | ||
| 647 | ;; X = P * CX | ||
| 648 | (ctransferm | ||
| 649 | (5x5-log | ||
| 650 | "ca" | ||
| 651 | (math-mul | ||
| 652 | inv-base-change | ||
| 653 | (math-mul transferm base-change)))); CA | ||
| 654 | (ctarget | ||
| 655 | (5x5-log | ||
| 656 | "cb" | ||
| 657 | (math-mul inv-base-change targetv))); CB | ||
| 658 | (row-1 (math-make-intv 3 1 transferm-kernel-size)) ; 1..2 | ||
| 659 | (row-2 (math-make-intv 1 transferm-kernel-size | ||
| 660 | grid-size-squared)); 3..25 | ||
| 661 | (col-1 (math-make-intv 3 1 (- grid-size-squared | ||
| 662 | transferm-kernel-size))); 1..23 | ||
| 663 | (col-2 (math-make-intv 1 (- grid-size-squared | ||
| 664 | transferm-kernel-size) | ||
| 665 | grid-size-squared)); 24..25 | ||
| 666 | (ctransferm-1-: (calcFunc-mrow ctransferm row-1)) | ||
| 667 | (ctransferm-1-1 (calcFunc-mcol ctransferm-1-: col-1)) | ||
| 668 | |||
| 669 | ;; By construction ctransferm-:-2 = 0, so ctransferm-1-2 = 0 | ||
| 670 | ;; and ctransferm-2-2 = 0. | ||
| 671 | |||
| 672 | ;;(ctransferm-1-2 (calcFunc-mcol ctransferm-1-: col-2)) | ||
| 673 | (ctransferm-2-: (calcFunc-mrow ctransferm row-2)) | ||
| 674 | (ctransferm-2-1 | ||
| 675 | (5x5-log | ||
| 676 | "ca_2_1" | ||
| 677 | (calcFunc-mcol ctransferm-2-: col-1))) | ||
| 678 | |||
| 679 | ;; By construction ctransferm-2-2 = 0. | ||
| 680 | ;; | ||
| 681 | ;;(ctransferm-2-2 (calcFunc-mcol ctransferm-2-: col-2)) | ||
| 682 | |||
| 683 | (ctarget-1 (calcFunc-mrow ctarget row-1)) | ||
| 684 | (ctarget-2 (calcFunc-mrow ctarget row-2)) | ||
| 685 | |||
| 686 | ;; ctarget-1(2x1) = ctransferm-1-1(2x23) *cx-1(23x1) | ||
| 687 | ;; + ctransferm-1-2(2x2) *cx-2(2x1); | ||
| 688 | ;; ctarget-2(23x1) = ctransferm-2-1(23x23)*cx-1(23x1) | ||
| 689 | ;; + ctransferm-2-2(23x2)*cx-2(2x1); | ||
| 690 | ;; By construction: | ||
| 691 | ;; | ||
| 692 | ;; ctransferm-1-2 == zeros(2,2) and ctransferm-2-2 == zeros(23,2) | ||
| 693 | ;; | ||
| 694 | ;; So: | ||
| 695 | ;; | ||
| 696 | ;; ctarget-2 = ctransferm-2-1*cx-1 | ||
| 697 | ;; | ||
| 698 | ;; So: | ||
| 699 | ;; | ||
| 700 | ;; cx-1 = inv-ctransferm-2-1 * ctarget-2 | ||
| 701 | (cx-1 (math-mul (calcFunc-inv ctransferm-2-1) ctarget-2)) | ||
| 702 | |||
| 703 | ;; Any cx-2 can do, so there are 2^{transferm-kernel-size} solutions. | ||
| 704 | (solution-list | ||
| 705 | ;; Within solution-list each element is a cons cell: | ||
| 706 | ;; | ||
| 707 | ;; (HW . SOL) | ||
| 708 | ;; | ||
| 709 | ;; where HW is the Hamming weight of solution, and SOL is | ||
| 710 | ;; the solution in the form of a grid. | ||
| 711 | (sort | ||
| 712 | (cdr | ||
| 713 | (math-map-vec | ||
| 714 | (lambda (cx-2) | ||
| 715 | ;; Compute `solution' in the form of a 25x1 matrix of | ||
| 716 | ;; (mod B 2) forms --- with B = 0 or 1 --- and | ||
| 717 | ;; return (HW . SOL) where HW is the Hamming weight | ||
| 718 | ;; of solution and SOL a grid. | ||
| 719 | (let ((solution (math-mul | ||
| 720 | base-change | ||
| 721 | (calcFunc-vconcat cx-1 cx-2)))); X = P * CX | ||
| 722 | (cons | ||
| 723 | ;; The Hamming Weight is computed by matrix reduction | ||
| 724 | ;; with an ad-hoc operator. | ||
| 725 | (math-reduce-vec | ||
| 726 | ;; (cadadr '(vec (mod x 2))) => x | ||
| 727 | (lambda (r x) (+ (if (integerp r) r (cadadr r)) | ||
| 728 | (cadadr x))) | ||
| 729 | solution); car | ||
| 730 | (5x5-vec-to-grid | ||
| 731 | (calcFunc-arrange solution 5x5-grid-size));cdr | ||
| 732 | ))) | ||
| 733 | ;; A (2^K) x K matrix, where K is the dimension of kernel | ||
| 734 | ;; of transfer matrix --- i.e. K=2 in if the grid is 5x5 | ||
| 735 | ;; --- for I from 0 to K-1, each row rI correspond to the | ||
| 736 | ;; binary representation of number I, that is to say row | ||
| 737 | ;; rI is a 1xK vector: | ||
| 738 | ;; [ n{I,0} n{I,1} ... n{I,K-1} ] | ||
| 739 | ;; such that: | ||
| 740 | ;; I = sum for J=0..K-1 of 2^(n{I,J}) | ||
| 741 | (let ((calc-number-radix 2) | ||
| 742 | (calc-leading-zeros t) | ||
| 743 | (calc-word-size transferm-kernel-size)) | ||
| 744 | (math-map-vec | ||
| 745 | (lambda (x) | ||
| 746 | (cons 'vec | ||
| 747 | (mapcar (lambda (x) `(vec (mod ,(logand x 1) 2))) | ||
| 748 | (substring (math-format-number x) | ||
| 749 | (- transferm-kernel-size))))) | ||
| 750 | (calcFunc-index (math-pow 2 transferm-kernel-size) 0))) )) | ||
| 751 | ;; Sort solutions according to respective Hamming weight. | ||
| 752 | (lambda (x y) (< (car x) (car y))) | ||
| 753 | ))) | ||
| 754 | (message "5x5 Solution computation done.") | ||
| 755 | solution-list))) | ||
| 756 | |||
| 757 | (defun 5x5-solve-suggest (&optional n) | ||
| 758 | "Suggest to the user where to click. | ||
| 759 | |||
| 760 | Argument N is ignored." | ||
| 761 | ;; For the time being n is ignored, the idea was to use some numeric | ||
| 762 | ;; argument to show a limited amount of positions. | ||
| 763 | (interactive "P") | ||
| 764 | (5x5-log-init) | ||
| 765 | (let ((solutions (5x5-solver 5x5-grid))) | ||
| 766 | (setq 5x5-solver-output | ||
| 767 | (cons 5x5-moves solutions))) | ||
| 768 | (5x5-draw-grid (list 5x5-grid)) | ||
| 769 | (5x5-position-cursor)) | ||
| 770 | |||
| 418 | ;; Keyboard response functions. | 771 | ;; Keyboard response functions. |
| 419 | 772 | ||
| 420 | (defun 5x5-flip-current () | 773 | (defun 5x5-flip-current () |
| @@ -490,7 +843,7 @@ in progress because it is an animated attempt." | |||
| 490 | (setq 5x5-x-pos (/ 5x5-grid-size 2) | 843 | (setq 5x5-x-pos (/ 5x5-grid-size 2) |
| 491 | 5x5-y-pos (/ 5x5-grid-size 2) | 844 | 5x5-y-pos (/ 5x5-grid-size 2) |
| 492 | 5x5-moves 0 | 845 | 5x5-moves 0 |
| 493 | 5x5-grid (5x5-make-random-grid)) | 846 | 5x5-grid (5x5-make-random-grid (symbol-function '5x5-make-move))) |
| 494 | (unless 5x5-cracking | 847 | (unless 5x5-cracking |
| 495 | (5x5-draw-grid (list 5x5-grid))) | 848 | (5x5-draw-grid (list 5x5-grid))) |
| 496 | (5x5-position-cursor))) | 849 | (5x5-position-cursor))) |