24
\$\begingroup\$

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))
Toby Speight
87.2k14 gold badges104 silver badges322 bronze badges
asked Jan 11, 2018 at 12:30
\$\endgroup\$
2
  • 7
    \$\begingroup\$ rubik's cube. \$\endgroup\$ Commented Jan 11, 2018 at 13:12
  • 3
    \$\begingroup\$ That's an embarassing failure on my part \$\endgroup\$ Commented Jan 11, 2018 at 19:42

1 Answer 1

3
\$\begingroup\$

First, congrats on writing a Rubik's Cube mode. That is a feat. There are, however, three parts I would love to see improved in a follow-up version:

  1. Currently, none of the symbols are private; functions and variables that should not be used outside your module usually have two hyphens, e.g., rubic--paint-diamond. As far as I understood, almost all your functions and values should be considered private, other than the (interactive) commands.
  2. The current variant is not consistent in its use of documentation strings. I'd argue that all functions should be documented there.
  3. This is probably the most important remark: your code does not work. You're missing (require 'cl). But since Emacs 27, cl is deprecated. You should (require 'cl-lib) and prefix the CL functions with cl (e.g., replace loop with cl-loop).

Other than that, there are some typos, e.g., rubic-cuve-back or te. Those should probably get fixed.

From a usability point of view, it would be great if the key bindings were listed below the history. That would ease the usage. Also, you should probably not bind g to a reset, at least not without a yes-or-no prompt.

Hope that helps!

Toby Speight
87.2k14 gold badges104 silver badges322 bronze badges
answered Oct 13, 2024 at 18:53
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.