Following is a dumb implementation of Rubik's cube for Emacs. I am not well versed in writing (e)lisp, so I ask for a review and corrections.
;; -*- lexical-binding: t; -*- ;;; rubic_cube.el ;; emulation and solution of rubic's cube in emacs ;; cube layout (defconst rubic-cube-front-default '(" / \\ " " / \\ " " / \\ / \\ " " / \\_/ \\ " " / \\ / \\ / \\ " " / \\_/ \\_/ \\ " " |\\_ / \\ / \\ /| " " | \\ / \\_/ \\_/ | " " | |\\_ / \\ /| | " " |\\_ | \\ / \\_/ | _/| " " | \\| |\\_ _/| |/ | " " | |\\_ | \\ / | _/| | " " |\\_ | \\| | |/ | _/| " " | \\| |\\ | /| |/ | " " | |\\_ | \\|/ | _/| | " " \\_ | \\| | |/ | _/ " " \\| |\\_ | _/| |/ " " \\_ | \\|/ | _/ " " \\| | |/ " " \\_ | _/ " " \\|/ ")) (defconst rubic-cube-back-default '(" _/|\\_ " " / | \\ " " _/| | |\\_ " " / | _/|\\_ | \\ " " _/| |/ | \\| |\\_ " " / | _/| | |\\_ | \\ " " | |/ | _/|\\_ | \\| | " " | _/| |/ | \\| |\\_ | " " |/ | _/| | |\\_ | \\| " " | |/ | _/ \\_ | \\| | " " | _/| |/ \\| |\\_ | " " |/ | _/ \\_ _/ \\_ | \\| " " | |/ \\_/ \\| | " " | _/ \\_ / \\_ _/ \\_ | " " |/ \\_/ \\_/ \\| " " \\_ _/ \\_ _/ \\_ _/ " " \\ / \\_/ \\_/ " " \\_ _/ \\_ _/ " " \\ / \\_/ " " \\_ _/ " " \\_/ ")) (defvar-local rubic-cuve-back nil "Canbas for front of rubic's cube") (defvar-local rubic-cube-front nil "Canvas for back of rubic's cube") (defconst side-coord '[(0 14) (7 3) (11 17) (5 5) (1 15) (9 14)] "Coordinates of starting points of the cube sides") ;; faces (defface rubic-red '((t . (:background "red"))) "Red colored cube side") (defface rubic-orange '((t . (:background "orange"))) "Orange colored cube side") (defface rubic-white '((t . (:background "antique white"))) "white colored cube side") (defface rubic-blue '((t . (:background "blue"))) "Blue colored cube side") (defface rubic-yellow '((t . (:background "yellow"))) "Yellow colored cube side") (defface rubic-green '((t . (:background "green"))) "green colored cube side") (defconst rubic-faces [rubic-red rubic-white rubic-green rubic-yellow rubic-blue rubic-orange]) ;; painter functions (defun rubic-color-painter (face) "make function that will apply color to string" (lambda (str beg end prn) (add-face-text-property beg end face t str))) (defun rubic-paint-diamond (cube-screen coord painter) "Paint diamond shape with provided paint function" (let ((l (first coord)) (c (second coord))) (apply painter (nth l cube-screen) c (1+ c) nil nil) (apply painter (nth (incf l) cube-screen) (- c 2) (+ c 3) t nil) (apply painter (nth (incf l) cube-screen) (- c 2) (+ c 3) nil nil) (apply painter (nth (incf l) cube-screen) c (1+ c) nil nil))) (defun rubic-paint-slope-down (cube-screen coord painter) "Paint downward slope with provided paint function" (let ((l (first coord)) (c (second coord))) (apply painter (nth l cube-screen) c (+ c 2) nil nil) (apply painter (nth (incf l) cube-screen) c (+ c 3) t nil) (apply painter (nth (incf l) cube-screen) (1+ c) (+ c 3) nil nil))) (defun rubic-paint-slope-up (cube-screen coord painter) "Paint upwards slope with provided paint function" (let ((l (first coord)) (c (second coord))) (apply painter (nth l cube-screen) (1- c) (1+ c) nil nil) (apply painter (nth (incf l) cube-screen) (- c 2) (1+ c) t nil) (apply painter (nth (incf l) cube-screen) (- c 2) c nil nil))) (defun rubic-make-initial-cube () (loop with res = (make-vector (* 9 6) 0) for i from 0 to 5 do (loop for j from 0 to 8 do (aset res (+ j (* i 9)) i)) finally return res)) (defun rubic-displace-diamond (coord local-number) "Calculate displacement of the cell of top and bottom sides" (let ((l (first coord)) (c (second coord)) (row (/ local-number 3)) (col (mod local-number 3))) (setq l (+ l (* 2 row))) (setq c (- c (* 4 row))) (setq l (+ l (* 2 col))) (setq c (+ c (* 4 col))) (list l c))) (defun rubic-displace-downslope (coord local-number) "Calculate displacements of te cell of front and left sides" (let ((l (first coord)) (c (second coord)) (row (/ local-number 3)) (col (mod local-number 3))) (setq l (+ l (* 3 row))) (setq l (+ l (* 2 col))) (setq c (+ c (* 4 col))) (list l c))) (defun rubic-displace-upslope (coord local-number) "Calculate displacements of te cell of front and left sides" (let ((l (first coord)) (c (second coord)) (row (/ local-number 3)) (col (mod local-number 3))) (setq l (+ l (* 3 row))) (setq l (- l (* 2 col))) (setq c (+ c (* 4 col))) (list l c))) (defun rubic-get-local-number (cell-number) (list (/ cell-number 9) (mod cell-number 9))) (defun rubic-paint-cell (front back number color) (destructuring-bind (side local-number) (rubic-get-local-number number) (let ((canvas (if (< side 3) front back)) (painter (rubic-color-painter (aref rubic-faces color))) (filler (case side ((0 5) 'rubic-paint-diamond) ((1 4) 'rubic-paint-slope-down) ((2 3) 'rubic-paint-slope-up))) (translator (case side ((0 5) 'rubic-displace-diamond) ((1 4) 'rubic-displace-downslope) ((2 3) 'rubic-displace-upslope)))) (apply filler canvas (apply translator (aref side-coord side) local-number nil) painter nil)))) (defun rubic-paint-cube (cube-state) (setq rubic-cube-front (mapcar #'copy-sequence rubic-cube-front-default)) (setq rubic-cube-back (mapcar #'copy-sequence rubic-cube-back-default)) (loop for i from 1 to (* 6 9) do (rubic-paint-cell rubic-cube-front rubic-cube-back (1- i) (aref cube-state (1- i))))) ;; definitions for rotational notation (require 'calc) (defconst rubic-i '(cplx 0 1)) (defconst rubic-neg-i '(cplx 0 -1)) (defconst rubic-desc-rot-identity '((0 1) (1 1) (2 1) (3 1) (4 1) (5 1))) (defconst rubic-desc-rot-1-2 `((1 1) (5 ,rubic-i) (2 ,rubic-neg-i) (0 -1) (4 ,rubic-i) (3 ,rubic-i))) (defconst rubic-desc-rot-1-3 `((2 ,rubic-i) (1 ,rubic-i) (5 -1) (3 ,rubic-neg-i) (0 ,rubic-i) (4 1))) (defconst rubic-desc-rot-2-3 `((0 ,rubic-neg-i) (2 1) (3 1) (4 1) (1 1) (5 ,rubic-i))) (defun rubic-compose-rot (rot-1 rot-2) "Make rotation from two defined ones" (loop for (n1 r1) in rot-1 for (n2 r2) = (nth n1 rot-2) collect (list n2 (math-mul r1 r2)))) (defun rubic-rotate (&rest turns) "Compose as many rotations as needed" (reduce 'rubic-compose-rot turns)) (defconst rubicside-renumbering-clockwise '(6 3 0 7 4 1 8 5 2)) (defun rubic-compose-subst (sub1 sub2) (loop for i1 in sub1 for i2 = (nth i1 sub2) collect i2)) (defun rubic-substitute (&rest subs) (reduce 'rubic-compose-subst subs)) (defconst rubic-side-rotations (list (cons rubic-i rubicside-renumbering-clockwise) (cons -1 (rubic-substitute rubicside-renumbering-clockwise rubicside-renumbering-clockwise)) (cons rubic-neg-i (rubic-substitute rubicside-renumbering-clockwise rubicside-renumbering-clockwise rubicside-renumbering-clockwise)) ;; we could call with 4 rotations or define a constant for identity. But therte is no need, really (cons 1 (list 0 1 2 3 4 5 6 7 8)))) (defun rubic-expand-substitution (sub) "Get substitution for entire cube state" (loop with res = (make-vector (* 9 6) 0) for i from 0 to 5 for (side-number rot) = (nth i sub) for side-reorder = (cdr (assoc rot rubic-side-rotations)) for base-offset = (* i 9) for target-offset = (* side-number 9) do (loop for j from 0 to 8 do (setf (aref res (+ j target-offset)) (+ base-offset (nth j side-reorder)))) finally return (append res nil))) (defconst rubic-turn-top-substitution (let ((temp (coerce (loop for i from 0 to (1- (* 9 6)) collect i) 'vector)) (side-offsets '((9 36) (18 9) (27 18) (36 27))) (rotation (rubic-substitute rubicside-renumbering-clockwise rubicside-renumbering-clockwise rubicside-renumbering-clockwise))) (loop for i from 0 to 8 do (aset temp i (nth i rotation))) (loop for (loc num) in side-offsets do (loop for i from 0 to 2 do (aset temp (+ loc i) (+ num i)))) (append temp nil))) (defun rubic-apply-transformation (state transform) (let ((second (copy-sequence state))) (loop for i from 0 to (1- (length second)) do (aset state (nth i transform) (aref second i))))) ;; definitions of common transformations (defconst rubic-U rubic-turn-top-substitution) (defconst rubic-U2 (rubic-substitute rubic-turn-top-substitution rubic-turn-top-substitution)) (defconst rubic-Ui (rubic-substitute rubic-turn-top-substitution rubic-turn-top-substitution rubic-turn-top-substitution)) (defconst rubic-F (rubic-substitute (rubic-expand-substitution rubic-desc-rot-1-2) rubic-turn-top-substitution (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-2 rubic-desc-rot-1-2 rubic-desc-rot-1-2)))) (defconst rubic-F2 (rubic-substitute (rubic-expand-substitution rubic-desc-rot-1-2) rubic-turn-top-substitution rubic-turn-top-substitution (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-2 rubic-desc-rot-1-2 rubic-desc-rot-1-2)))) (defconst rubic-Fi (rubic-substitute (rubic-expand-substitution rubic-desc-rot-1-2) rubic-turn-top-substitution rubic-turn-top-substitution rubic-turn-top-substitution (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-2 rubic-desc-rot-1-2 rubic-desc-rot-1-2)))) (defconst rubic-R (rubic-substitute (rubic-expand-substitution rubic-desc-rot-1-3) rubic-turn-top-substitution (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-3 rubic-desc-rot-1-3 rubic-desc-rot-1-3)))) (defconst rubic-R2 (rubic-substitute (rubic-expand-substitution rubic-desc-rot-1-3) rubic-turn-top-substitution rubic-turn-top-substitution (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-3 rubic-desc-rot-1-3 rubic-desc-rot-1-3)))) (defconst rubic-Ri (rubic-substitute (rubic-expand-substitution rubic-desc-rot-1-3) rubic-turn-top-substitution rubic-turn-top-substitution rubic-turn-top-substitution (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-3 rubic-desc-rot-1-3 rubic-desc-rot-1-3)))) (defconst rubic-L (rubic-substitute (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-3 rubic-desc-rot-1-3 rubic-desc-rot-1-3)) rubic-turn-top-substitution (rubic-expand-substitution rubic-desc-rot-1-3))) (defconst rubic-L2 (rubic-substitute (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-3 rubic-desc-rot-1-3 rubic-desc-rot-1-3)) rubic-turn-top-substitution rubic-turn-top-substitution (rubic-expand-substitution rubic-desc-rot-1-3))) (defconst rubic-Li (rubic-substitute (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-3 rubic-desc-rot-1-3 rubic-desc-rot-1-3)) rubic-turn-top-substitution rubic-turn-top-substitution rubic-turn-top-substitution (rubic-expand-substitution rubic-desc-rot-1-3))) (defconst rubic-B (rubic-substitute (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-2 rubic-desc-rot-1-2 rubic-desc-rot-1-2)) rubic-turn-top-substitution (rubic-expand-substitution rubic-desc-rot-1-2))) (defconst rubic-B2 (rubic-substitute (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-2 rubic-desc-rot-1-2 rubic-desc-rot-1-2)) rubic-turn-top-substitution rubic-turn-top-substitution (rubic-expand-substitution rubic-desc-rot-1-2))) (defconst rubic-Bi (rubic-substitute (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-2 rubic-desc-rot-1-2 rubic-desc-rot-1-2)) rubic-turn-top-substitution rubic-turn-top-substitution rubic-turn-top-substitution (rubic-expand-substitution rubic-desc-rot-1-2))) (defconst rubic-D (rubic-substitute (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-2 rubic-desc-rot-1-2)) rubic-turn-top-substitution (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-2 rubic-desc-rot-1-2)))) (defconst rubic-D2 (rubic-substitute (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-2 rubic-desc-rot-1-2)) rubic-turn-top-substitution rubic-turn-top-substitution (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-2 rubic-desc-rot-1-2)))) (defconst rubic-Di (rubic-substitute (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-2 rubic-desc-rot-1-2)) rubic-turn-top-substitution rubic-turn-top-substitution rubic-turn-top-substitution (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-2 rubic-desc-rot-1-2)))) (defconst rubic-x (rubic-expand-substitution rubic-desc-rot-1-2)) (defconst rubic-x2 (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-2 rubic-desc-rot-1-2))) (defconst rubic-xi (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-2 rubic-desc-rot-1-2 rubic-desc-rot-1-2))) (defconst rubic-y (rubic-expand-substitution rubic-desc-rot-2-3)) (defconst rubic-y2 (rubic-expand-substitution (rubic-rotate rubic-desc-rot-2-3 rubic-desc-rot-2-3))) (defconst rubic-yi (rubic-expand-substitution (rubic-rotate rubic-desc-rot-2-3 rubic-desc-rot-2-3 rubic-desc-rot-2-3))) (defconst rubic-zi (rubic-expand-substitution rubic-desc-rot-1-3)) (defconst rubic-z2 (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-3 rubic-desc-rot-1-3))) (defconst rubic-z (rubic-expand-substitution (rubic-rotate rubic-desc-rot-1-3 rubic-desc-rot-1-3 rubic-desc-rot-1-3))) ;; mode definitions (defvar-local cube-state nil "State of the cube that is being processed") (defvar-local cube-undo (list 'undo) "List of previously executed commands") (defvar-local cube-redo (list 'redo) "List of previously undone commands") (defun rubic-center-string (str l) (if (> (length str) l) (substring str l) (let* ((sl (length str)) (pad (/ (- l sl) 2)) (rem (- l pad))) (concat (format (format "%%%ds" rem) str) (make-string pad ? ))))) (defun rubic-display-cube () "Draw current cube state, assuming empty buffer" ;; first, update canvasses according to the cube state (rubic-paint-cube cube-state) (let ((w (length (first rubic-cube-front-default))) (h (length rubic-cube-front-default))) (insert (format "%s%s\n" (rubic-center-string "*FRONT*" w) (rubic-center-string "*BACK*" w))) (insert-rectangle rubic-cube-front) (forward-line (- (decf h))) (move-end-of-line nil) (insert-rectangle rubic-cube-back))) (defun rubic-display-undo () "Print undo information" (loop with line-str = "\nUndo: " for cmd in (reverse (cdr cube-undo)) for i = 1 then (1+ i) do (progn (setq line-str (concat line-str (format "%d. %s " i (get cmd 'name)))) (when (> (length line-str) fill-column) (insert line-str) (setq line-str (concat "\n" (make-string 6 ? ))))) finally (insert line-str))) (defun rubic-display-redo () "Print undo information" (loop with line-str = "\nRedo: " for cmd in (cdr cube-redo) for i = 1 then (1+ i) do (progn (setq line-str (concat line-str (format "%d. %s " i (get cmd 'name)))) (when (> (length line-str) fill-column) (insert line-str) (setq line-str (concat "\n" (make-string 6 ? ))))) finally (insert line-str))) (defun rubic-draw-all () (setq buffer-read-only nil) (erase-buffer) (rubic-display-cube) (rubic-display-undo) (rubic-display-redo) (setq buffer-read-only t)) (defmacro rubic-define-command (transformation desc) (let ((command-name (intern (concat (symbol-name transformation) "-command")))) (put command-name 'name desc) `(defun ,command-name (&optional arg) (interactive) (rubic-apply-transformation cube-state ,transformation) (unless arg (setcdr cube-redo nil) (setcdr cube-undo (cons (quote ,command-name) (cdr cube-undo))) (rubic-draw-all))))) (defmacro rubic-define-commands (&rest transformations) (let ((head (car transformations)) (command-name (cadr transformations)) (tail (cddr transformations))) `(progn ,(when head `(rubic-define-command ,head ,command-name)) ,(when tail `(rubic-define-commands ,@tail))))) (rubic-define-commands rubic-U "Upper" rubic-U2 "Upper twice" rubic-Ui "Upper inverted" rubic-F "Front" rubic-F2 "Front twice" rubic-Fi "Front inverted" rubic-R "Right" rubic-R2 "Right twice" rubic-Ri "Right inverted" rubic-L "Left" rubic-L2 "Left twice" rubic-Li "Left inverted" rubic-B "Back" rubic-B2 "Back twice" rubic-Bi "Back inverted" rubic-D "Down" rubic-D2 "Down twice" rubic-Di "Down inverted" rubic-x "X rotation" rubic-x2 "X overturn" rubic-xi "X rotation inverted" rubic-y "Y rotation" rubic-y2 "Y overturn" rubic-yi "Y rotation inverted" rubic-z "Z rotation" rubic-z2 "Z overturn" rubic-zi "Z rotation inverted") (defconst rubic-reverse-commands '((rubic-U-command . rubic-Ui-command) (rubic-U2-command . rubic-U2-command) (rubic-F-command . rubic-Fi-command) (rubic-F2-command . rubic-F2-command) (rubic-R-command . rubic-Ri-command) (rubic-R2-command . rubic-R2-command) (rubic-L-command . rubic-Li-command) (rubic-L2-command . rubic-L2-command) (rubic-B-command . rubic-Bi-command) (rubic-B2-command . rubic-B2-command) (rubic-D-command . rubic-Di-command) (rubic-D2-command . rubic-D2-command) (rubic-x-command . rubic-xi-command) (rubic-x2-command . rubic-x2-command) (rubic-y-command . rubic-yi-command) (rubic-y2-command . rubic-y2-command) (rubic-z-command . rubic-zi-command) (rubic-z2-command . rubic-z2-command))) (defun rubic-reset () "Set cube to initial state" (interactive) (setq cube-state (rubic-make-initial-cube)) (setq cube-undo (list 'undo)) (setq cube-redo (list 'redo)) (rubic-draw-all)) (defun rubic-undo (&optional arg) "Undo up to ARG commands from undo list" (interactive "p") (let ((num (or arg 1))) (loop repeat num for lastcmd = (cadr cube-undo) when lastcmd do (let ((revcmd (or (cdr (assoc lastcmd rubic-reverse-commands)) (car (rassoc lastcmd rubic-reverse-commands))))) (setcdr cube-redo (cons lastcmd (cdr cube-redo))) (setcdr cube-undo (cddr cube-undo)) (apply revcmd '(t)))) (rubic-draw-all))) (defun rubic-redo (&optional arg) "Redo up to ARG commands" (interactive "p") (let ((num (or arg 1))) (loop repeat num for lastcmd = (cadr cube-redo) when lastcmd do (progn (setcdr cube-undo (cons lastcmd (cdr cube-undo))) (setcdr cube-redo (cddr cube-redo)) (apply lastcmd '(t)))) (rubic-draw-all))) (defvar rubic-mode-map (let ((map (make-sparse-keymap)) (map-i (make-sparse-keymap)) (map-2 (make-sparse-keymap))) (define-key map "u" 'rubic-U-command) (define-key map-2 "u" 'rubic-U2-command) (define-key map-i "u" 'rubic-Ui-command) (define-key map "f" 'rubic-F-command) (define-key map-2 "f" 'rubic-F2-command) (define-key map-i "f" 'rubic-Fi-command) (define-key map "r" 'rubic-R-command) (define-key map-2 "r" 'rubic-R2-command) (define-key map-i "r" 'rubic-Ri-command) (define-key map "l" 'rubic-L-command) (define-key map-2 "l" 'rubic-L2-command) (define-key map-i "l" 'rubic-Li-command) (define-key map "b" 'rubic-B-command) (define-key map-2 "b" 'rubic-B2-command) (define-key map-i "b" 'rubic-Bi-command) (define-key map "d" 'rubic-D-command) (define-key map-2 "d" 'rubic-D2-command) (define-key map-i "d" 'rubic-Di-command) (define-key map "x" 'rubic-x-command) (define-key map-2 "x" 'rubic-x2-command) (define-key map-i "x" 'rubic-xi-command) (define-key map "y" 'rubic-y-command) (define-key map-2 "y" 'rubic-y2-command) (define-key map-i "y" 'rubic-yi-command) (define-key map "z" 'rubic-z-command) (define-key map-2 "z" 'rubic-z2-command) (define-key map-i "z" 'rubic-zi-command) (define-key map "g" 'rubic-reset) (define-key map "2" map-2) (define-key map "i" map-i) (define-key map (kbd "M-u") 'rubic-undo) (define-key map (kbd "M-r") 'rubic-redo) map)) (define-derived-mode rubic-mode special-mode "rubic's cube") (add-hook 'rubic-mode-hook (lambda () (rubic-reset))) (defun rubic () "Prepare for rubic mode" (interactive) (switch-to-buffer "*Rubic*") (rubic-mode))