<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/assets/atom.xsl" type="text/xsl"?><feed
	xmlns="http://www.w3.org/2005/Atom"
	xmlns:thr="http://purl.org/syndication/thread/1.0"
	xml:lang="en-US"
	><title>Sacha Chua - category - cubing</title>
	<subtitle>Emacs, sketches, and life</subtitle>
	<link rel="self" type="application/atom+xml" href="https://sachachua.com/blog/category/cubing/feed/atom/index.xml" />
  <link rel="alternate" type="text/html" href="https://sachachua.com/blog/category/cubing" />
  <id>https://sachachua.com/blog/category/cubing/feed/atom/index.xml</id>
  <generator uri="https://11ty.dev">11ty</generator>
	<updated>2024-03-06T13:12:33Z</updated>
<entry>
		<title type="html">Cubing and Emacs: Checking out the competition</title>
		<link rel="alternate" type="text/html" href="https://sachachua.com/blog/2024/03/cubing-and-emacs-checking-out-the-competition/"/>
		<author><name><![CDATA[Sacha Chua]]></name></author>
		<updated>2024-03-06T13:12:33Z</updated>
    <published>2024-03-06T13:12:33Z</published>
    <category term="cubing" />
<category term="emacs" />
		<id>https://sachachua.com/blog/2024/03/cubing-and-emacs-checking-out-the-competition/</id>
		<content type="html"><![CDATA[<p>
There's an upcoming cubing competition. For the
3x3 event, the top 75% of the first round will get
to advance to the second round. I wanted to know
what our chances were of making it in. There's an
<a href="https://github.com/robiningelbrecht/wca-rest-api">unofficial REST API</a> for the <a href="https://www.worldcubeassociation.org/export/results">World Cube Association (WCA) results</a> so I don't need to download and
unpack megabytes of data.
</p>

<p>
Here's the basic code I used:
</p>


<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">let*</span> ((comp-id <span class="org-string">"COMP-ID-GOES-HERE"</span>)
       (url (format <span class="org-string">"https://www.worldcubeassociation.org/competitions/%s/registrations"</span> comp-id))
       (dom (<span class="org-keyword">with-temp-buffer</span> (insert (plz <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">get</span> url)) (libxml-parse-html-region)))
       (data-dir (expand-file-name <span class="org-string">"data"</span> (expand-file-name comp-id <span class="org-string">"~/proj/cubing/"</span>)))
       len
       list)
  (<span class="org-keyword">unless</span> (file-directory-p data-dir)
    (make-directory data-dir t))
  (<span class="org-keyword">setq</span> list (sort
              (seq-keep
               (<span class="org-keyword">lambda</span> (o)
                 (<span class="org-keyword">let*</span> ((id (replace-regexp-in-string <span class="org-string">"/persons/"</span> <span class="org-string">""</span> (<span class="org-keyword">dom-attr</span> o <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">href</span>)))
                        (file (expand-file-name (concat id <span class="org-string">".json"</span>) data-dir))
                        best)
                   (<span class="org-keyword">unless</span> (file-exists-p file)
                     (<span class="org-keyword">with-temp-file</span> file
                       (insert
                        (plz <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">get</span> (format <span class="org-string">"https://raw.githubusercontent.com/robiningelbrecht/wca-rest-api/master/api/persons/%s.json"</span> id)))))
                   (<span class="org-keyword">let-alist</span> (<span class="org-keyword">with-temp-buffer</span>
                                (insert-file-contents file)
                                (json-parse-buffer <span class="org-builtin">:object-type</span> <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">alist</span>))
                     (<span class="org-keyword">setq</span> best
                           (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">best</span>
                                      (seq-find (<span class="org-keyword">lambda</span> (o) (string= (alist-get <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">eventId</span> o) <span class="org-string">"333"</span>)) .rank.averages)))
                     (<span class="org-keyword">when</span> best
                       (list .id .name (format <span class="org-string">"%.2f"</span> (/ best 100.0)))))))
               (dom-by-tag (dom-by-id dom <span class="org-string">"competition-data"</span>) <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">a</span>))
              (<span class="org-keyword">lambda</span> (a b)
                (&lt; (string-to-number (elt a 2)) (string-to-number (elt b 2))))))
  (<span class="org-keyword">setq</span> len (length list))
  (seq-map-indexed
   (<span class="org-keyword">lambda</span> (o i)
     (cons (format <span class="org-string">"%d"</span> (/ (* 100.0 i) len))
           o))
   list))
</pre>
</div>


<p>
It makes a table with percentile, ID, name, and average time for 3x3. Then I can find out where the 75% mark and see if we can make it in. I think I'll be a bit too slow, but the kiddo might be able to make it to the second round. Could be fun.
</p>

<p>
Posting here in case someone else might find it handy someday!
</p>
<p>You can <a href="https://sachachua.com/blog/2024/03/cubing-and-emacs-checking-out-the-competition/#comment">view 1 comment</a> or <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2024%2F03%2Fcubing-and-emacs-checking-out-the-competition%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></content>
		</entry><entry>
		<title type="html">Using rubik.el to make SVG last-layer diagrams from algorithms</title>
		<link rel="alternate" type="text/html" href="https://sachachua.com/blog/2023/02/using-rubik-el-to-make-svg-last-layer-diagrams-from-algorithms/"/>
		<author><name><![CDATA[Sacha Chua]]></name></author>
		<updated>2023-02-09T19:19:31Z</updated>
    <published>2023-02-09T19:19:31Z</published>
    <category term="cubing" />
<category term="emacs" />
<category term="org" />
		<id>https://sachachua.com/blog/2023/02/using-rubik-el-to-make-svg-last-layer-diagrams-from-algorithms/</id>
		<content type="html"><![CDATA[<p>
So I checked out <a href="https://codeberg.org/akib/emacs-cube">emacs-cube</a>, but I had a hard time figuring out how to
work with the data model without getting into all the rendering
because it figures "left" and "right" based on camera position.
<a href="https://github.com/Kurvivor19/rubik-mode/blob/master/rubik.el">rubik.el</a> seemed like an easier starting point. As far as I can tell,
the <code>rubik-cube-state</code> local variable is an array with the faces
specified as 6 groups of 9 integers in this order: top, front, right,
back, left, bottom, with cells specified from left to right, top to
bottom.
</p>

<p>
First, I wanted to recolour <code>rubik</code> so that it matched the setup of
the Roofpig JS library I'm using for animations.
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">defconst</span> <span class="org-variable-name">my-cubing-rubik-faces</span> <span class="org-string">"YRGOBW"</span>)
<span class="org-comment-delimiter">;; </span><span class="org-comment">make it match roofpig's default setup with yellow on top and red in front</span>
(<span class="org-keyword">defconst</span> <span class="org-variable-name">rubik-faces</span> [rubik-yellow
                       rubik-red
                       rubik-green
                       rubik-orange
                       rubik-blue
                       rubik-white])
</pre>
</div>

<p>
Here are some functions to apply an algorithm (or actually, the inverse of the algorithm, which is useful for exploring a PLL case):
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">my-cubing-normalize</span> (alg)
  <span class="org-doc">"Remove parentheses and clean up spaces in ALG."</span>
  (string-trim
   (replace-regexp-in-string <span class="org-string">"[() ]+"</span> <span class="org-string">" "</span> alg)))

(<span class="org-keyword">defun</span> <span class="org-function-name">my-cubing-reverse-alg</span> (alg)
  <span class="org-doc">"Reverse the given ALG."</span>
  (mapconcat
   (<span class="org-keyword">lambda</span> (step)
     (<span class="org-keyword">if</span> (string-match <span class="org-string">"\\`</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">(</span></span><span class="org-string">[rludfsbRLUDFSBxyz]</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">)</span></span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">(</span></span><span class="org-string">['i]</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">)</span></span><span class="org-string">?\\'"</span> step)
         (concat (match-string 1 step)
                 (<span class="org-keyword">if</span> (match-string 2 step)
                     <span class="org-string">""</span>
                   <span class="org-string">"'"</span>))
       step))
   (reverse
    (split-string (my-cubing-normalize alg) <span class="org-string">" "</span>))
   <span class="org-string">" "</span>))

(<span class="org-keyword">defun</span> <span class="org-function-name">my-cubing-rubik-alg</span> (alg)
  <span class="org-doc">"Apply the reversed ALG to a solved cube.</span>
<span class="org-doc">Return the rubik.el cube state."</span>
  (<span class="org-keyword">let</span> ((reversed (my-cubing-reverse-alg alg)))
    (seq-reduce
     (<span class="org-keyword">lambda</span> (cube o)
       (<span class="org-keyword">when</span> (intern (format <span class="org-string">"rubik-%s"</span>
                             (replace-regexp-in-string <span class="org-string">"'"</span> <span class="org-string">"i"</span> o)))
         (<span class="org-keyword">unless</span> (string= o <span class="org-string">""</span>)
           (rubik-apply-transformation
            cube
            (symbol-value
             (intern
              (format <span class="org-string">"rubik-%s"</span>
                      (replace-regexp-in-string <span class="org-string">"'"</span> <span class="org-string">"i"</span> o)))))))
       cube)
     (split-string reversed <span class="org-string">" "</span>)
     (rubik-make-initial-cube))))
</pre>
</div>

<p>
Then I got the strings specifying the side colours and the top colours
in the format that I needed for the SVG diagrams. I'm optimistically
using <code>number-sequence</code> here instead of hard-coding the numbers so
that I can figure out how to extend the idea for 4x4 someday.
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">my-cubing-rubik-top-face-strings</span> (<span class="org-type">&amp;optional</span> cube)
  <span class="org-comment-delimiter">;; </span><span class="org-comment">edges starting from back left</span>
  (<span class="org-keyword">let</span> ((cube (<span class="org-keyword">or</span> cube rubik-cube-state)))
    (list
     (mapconcat
      (<span class="org-keyword">lambda</span> (i)
        (char-to-string (elt my-cubing-rubik-faces (aref cube i))))
      (append
       (reverse (number-sequence (* 3 9) (+ 2 (* 3 9))))
       (reverse (number-sequence (* 2 9) (+ 2 (* 2 9))))
       (reverse (number-sequence (* 1 9) (+ 2 (* 1 9))))
       (reverse (number-sequence (* 4 9) (+ 2 (* 4 9))))))
     (mapconcat
      (<span class="org-keyword">lambda</span> (i)
        (char-to-string (elt my-cubing-rubik-faces (aref cube i))))
      (number-sequence 0 8)))))
</pre>
</div>

<p>
Then theoretically, it can make a diagram like this:
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp">(<span class="org-keyword">defun</span> <span class="org-function-name">my-cubing-rubik-last-layer-with-sides-from-alg</span> (alg <span class="org-type">&amp;optional</span> arrows)
  (apply <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">my-cubing-last-layer-with-sides</span>
         (append
          (my-cubing-rubik-top-face-strings (my-cubing-rubik-alg alg))
          (list
           arrows))))
</pre>
</div>

<p>
So I can invoke it with:
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp">(my-cubing-rubik-last-layer-with-sides-from-alg
 <span class="org-string">"R U R' F' R U R' U' R' F R2 U' R' U'"</span>
 <span class="org-highlight-quoted-quote">'</span>((1 7 t) (2 8 t)))
</pre>
</div>


<figure id="org73b4cbb">
<img src="https://sachachua.com/blog/2023/02/using-rubik-el-to-make-svg-last-layer-diagrams-from-algorithms/last-layer.svg" alt="last-layer.svg" class="org-svg">

</figure>

<p>
It's also nice to be able to interactively step through the algorithm.
I prefer a more compact view of the undo/redo state.
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp"><span class="org-comment-delimiter">;; </span><span class="org-comment">Override undo information</span>
(<span class="org-keyword">defun</span> <span class="org-function-name">rubik-display-undo</span> ()
  <span class="org-doc">"Insert undo information at point."</span>
  (<span class="org-keyword">cl-loop</span> with line-str = <span class="org-string">"\nUndo: "</span>
           for cmd in (reverse (cdr rubik-cube-undo))
           for i = 1 then (1+ i)
           do (<span class="org-keyword">progn</span>
                (<span class="org-keyword">setq</span> line-str (concat line-str (format <span class="org-string">"%s "</span> (get cmd <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">name</span>))))
                (<span class="org-keyword">when</span> (&gt; (length line-str) fill-column)
                  (insert line-str)
                  (<span class="org-keyword">setq</span> line-str (concat <span class="org-string">"\n"</span> (make-string 6 ?\s)))))
           finally (insert line-str)))

<span class="org-comment-delimiter">;; </span><span class="org-comment">Override redo information</span>
(<span class="org-keyword">defun</span> <span class="org-function-name">rubik-display-redo</span> ()
  <span class="org-doc">"Insert redo information at point."</span>
  (<span class="org-keyword">cl-loop</span> with line-str = <span class="org-string">"\nRedo: "</span>
           for cmd in (cdr rubik-cube-redo)
           for i = 1 then (1+ i)
           do (<span class="org-keyword">progn</span>
                (<span class="org-keyword">setq</span> line-str (concat line-str (format <span class="org-string">"%s "</span> (get cmd <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">name</span>))))
                (<span class="org-keyword">when</span> (&gt; (length line-str) fill-column)
                  (insert line-str)
                  (<span class="org-keyword">setq</span> line-str (concat <span class="org-string">"\n"</span> (make-string 6 ?\s)))))
           finally (insert line-str)))
  
(<span class="org-keyword">defun</span> <span class="org-function-name">my-cubing-convert-alg-to-rubik-commands</span> (alg)
  (mapcar
   (<span class="org-keyword">lambda</span> (step)
     (intern
      (format <span class="org-string">"rubik-%s-command"</span>
              (replace-regexp-in-string <span class="org-string">"'"</span> <span class="org-string">"i"</span> step))))
   (split-string (my-cubing-normalize alg) <span class="org-string">" "</span>)))

(<span class="org-keyword">rubik-define-commands</span>
  rubik-U <span class="org-string">"U"</span> rubik-U2 <span class="org-string">"U2"</span> rubik-Ui <span class="org-string">"U'"</span>
  rubik-F <span class="org-string">"F"</span> rubik-F2 <span class="org-string">"F2"</span> rubik-Fi <span class="org-string">"F'"</span>
  rubik-R <span class="org-string">"R"</span> rubik-R2 <span class="org-string">"R2"</span> rubik-Ri <span class="org-string">"R'"</span>
  rubik-L <span class="org-string">"L"</span> rubik-L2 <span class="org-string">"L"</span> rubik-Li <span class="org-string">"L'"</span>
  rubik-B <span class="org-string">"B"</span> rubik-B2 <span class="org-string">"B"</span> rubik-Bi <span class="org-string">"B'"</span>
  rubik-D <span class="org-string">"D"</span> rubik-D2 <span class="org-string">"D"</span> rubik-Di <span class="org-string">"D'"</span>
  rubik-x <span class="org-string">"x"</span> rubik-x2 <span class="org-string">"x"</span> rubik-xi <span class="org-string">"x'"</span>
  rubik-y <span class="org-string">"y"</span> rubik-y2 <span class="org-string">"y"</span> rubik-yi <span class="org-string">"y'"</span>
  rubik-z <span class="org-string">"z"</span> rubik-z2 <span class="org-string">"z2"</span> rubik-zi <span class="org-string">"z'"</span>)

(<span class="org-keyword">defun</span> <span class="org-function-name">my-cubing-rubik-set-to-alg</span> (alg)
  (<span class="org-keyword">interactive</span> <span class="org-string">"MAlg: "</span>)
  (rubik)
  (fit-window-to-buffer)
  (<span class="org-keyword">setq</span> rubik-cube-state (my-cubing-rubik-alg alg))
  (<span class="org-keyword">setq</span> rubik-cube-redo (append (list <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">redo</span>)
                                (my-cubing-convert-alg-to-rubik-commands
                                 alg)))
  (<span class="org-keyword">setq</span> rubik-cube-undo <span class="org-highlight-quoted-quote">'</span>(undo))
  (rubik-draw-all)
  (display-buffer (current-buffer)))
</pre>
</div>

<p>
And now I can combine all those pieces together in a custom Org link type that will allow me to interactively step through an algorithm if I open it within Emacs and that will export to a diagram and an animation.
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp">(org-link-set-parameters
 <span class="org-string">"3x3"</span>
 <span class="org-builtin">:follow</span> <span class="org-highlight-quoted-quote">#'</span><span class="org-highlight-quoted-symbol">my-cubing-rubik-open</span>
 <span class="org-builtin">:export</span> <span class="org-highlight-quoted-quote">#'</span><span class="org-highlight-quoted-symbol">my-cubing-rubik-export</span>)

(<span class="org-keyword">defun</span> <span class="org-function-name">my-cubing-rubik-open</span> (path <span class="org-type">&amp;optional</span> _)
  (my-cubing-rubik-set-to-alg (<span class="org-keyword">if</span> (string-match <span class="org-string">"^</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">(</span></span><span class="org-string">.*</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">)</span></span><span class="org-string">\\?</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">(</span></span><span class="org-string">.*</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">)</span></span><span class="org-string">$"</span> path)
                                  (match-string 1 path)
                                path))) 
  
(<span class="org-keyword">defun</span> <span class="org-function-name">my-cubing-rubik-export</span> (path _ format _)
  <span class="org-doc">"Export PATH to FORMAT."</span>
  (<span class="org-keyword">let</span> (alg arrows params)
    (<span class="org-keyword">setq</span> alg path)
    (<span class="org-keyword">when</span> (string-match <span class="org-string">"^</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">(</span></span><span class="org-string">.*</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">)</span></span><span class="org-string">\\?</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">(</span></span><span class="org-string">.*</span><span class="org-string"><span class="org-regexp-grouping-backslash">\\</span></span><span class="org-string"><span class="org-regexp-grouping-construct">)</span></span><span class="org-string">$"</span> path)
      (<span class="org-keyword">setq</span> alg (match-string 1 path)
            params (org-protocol-convert-query-to-plist (match-string 2 path))
            arrows
            (mapcar (<span class="org-keyword">lambda</span> (entry)
                      (mapcar <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">string-to-number</span>
                               (split-string entry <span class="org-string">"-"</span>))) 
                    (split-string
                     (plist-get params <span class="org-builtin">:arrows</span>) <span class="org-string">","</span>))))
    (concat
     (my-cubing-rubik-last-layer-with-sides-from-alg
      alg
      arrows)
     (format <span class="org-string">"&lt;div class=\"roofpig\" data-config=\"base=PLL|alg=%s\"&gt;&lt;/div&gt;"</span>
             (my-cubing-normalize alg)))))
</pre>
</div>

<p>
Let's try that with this F-perm, which I haven't memorized yet:
</p>

<pre class="example" id="org995f475">
[[3x3:(R' U' F')(R U R' U')(R' F R2 U')(R' U' R U)(R' U R)?arrows=1-7,7-1,2-8,8-2]]
</pre>

<p>
<svg width="99" height="99" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <marker id="arrowhead" markerWidth="10" markerHeight="7" refX="0" refY="3.5" orient="auto-start-reverse"> <polygon fill="#000" points="0 0, 4 3.5, 0 7"></polygon></marker></defs> <rect width="26" height="10" x="10" y="0" fill="#ed7117" stroke="#666" stroke-width="1"></rect> <rect width="26" height="10" x="36" y="0" fill="#ff0000" stroke="#666" stroke-width="1"></rect> <rect width="26" height="10" x="62" y="0" fill="#00ff00" stroke="#666" stroke-width="1"></rect> <rect width="10" height="26" x="88" y="10" fill="#ff0000" stroke="#666" stroke-width="1"></rect> <rect width="10" height="26" x="88" y="36" fill="#00ff00" stroke="#666" stroke-width="1"></rect> <rect width="10" height="26" x="88" y="62" fill="#ed7117" stroke="#666" stroke-width="1"></rect> <rect width="26" height="10" x="62" y="88" fill="#00ff00" stroke="#666" stroke-width="1"></rect> <rect width="26" height="10" x="36" y="88" fill="#ed7117" stroke="#666" stroke-width="1"></rect> <rect width="26" height="10" x="10" y="88" fill="#ff0000" stroke="#666" stroke-width="1"></rect> <rect width="10" height="26" x="0" y="62" fill="#0000ff" stroke="#666" stroke-width="1"></rect> <rect width="10" height="26" x="0" y="36" fill="#0000ff" stroke="#666" stroke-width="1"></rect> <rect width="10" height="26" x="0" y="10" fill="#0000ff" stroke="#666" stroke-width="1"></rect> <rect width="26" height="26" x="10" y="10" stroke="#666" fill="#ffff00" stroke-width="1"></rect> <rect width="26" height="26" x="36" y="10" stroke="#666" fill="#ffff00" stroke-width="1"></rect> <rect width="26" height="26" x="62" y="10" stroke="#666" fill="#ffff00" stroke-width="1"></rect> <rect width="26" height="26" x="10" y="36" stroke="#666" fill="#ffff00" stroke-width="1"></rect> <rect width="26" height="26" x="36" y="36" stroke="#666" fill="#ffff00" stroke-width="1"></rect> <rect width="26" height="26" x="62" y="36" stroke="#666" fill="#ffff00" stroke-width="1"></rect> <rect width="26" height="26" x="10" y="62" stroke="#666" fill="#ffff00" stroke-width="1"></rect> <rect width="26" height="26" x="36" y="62" stroke="#666" fill="#ffff00" stroke-width="1"></rect> <rect width="26" height="26" x="62" y="62" stroke="#666" fill="#ffff00" stroke-width="1"></rect> <line x1="49" x2="49" y1="23" y2="75" marker-end="url(#arrowhead)" marker-start="nil" opacity="0.5" stroke="#000" stroke-width="2"></line> <line x1="49" x2="49" y1="75" y2="23" marker-end="url(#arrowhead)" marker-start="nil" opacity="0.5" stroke="#000" stroke-width="2"></line> <line x1="75" x2="75" y1="23" y2="75" marker-end="url(#arrowhead)" marker-start="nil" opacity="0.5" stroke="#000" stroke-width="2"></line> <line x1="75" x2="75" y1="75" y2="23" marker-end="url(#arrowhead)" marker-start="nil" opacity="0.5" stroke="#000" stroke-width="2"></line></svg></p><div class="roofpig" data-config="base=PLL|alg=R' U' F' R U R' U' R' F R2 U' R' U' R U R' U R"></div>
<p></p>

<p>
At some point, I'd like to change the display for rubik.el so that it
uses SVGs. (Or the OpenGL hacks in <a href="https://github.com/Jimx-/emacs-gl">https://github.com/Jimx-/emacs-gl</a>,
but that might be beyond my current ability.) In the meantime, this
might be fun.
</p>

<p>
In rubik.el, <code>M-r</code> redoes a move and <code>M-u</code> undoes it. Here's what it looks like with my tweaked interface:
</p>


<figure id="org8ad05ed">
<img src="https://sachachua.com/blog/2023/02/using-rubik-el-to-make-svg-last-layer-diagrams-from-algorithms/output-2023-02-09-15:25:42.gif" alt="output-2023-02-09-15:25:42.gif">

<figcaption><span class="figure-number">Figure 1: </span>Animated GIF of rubik.el stepping through an F-perm</figcaption>
</figure>



<style>
 .roofpig { max-width: 400px; margin-bottom: 80px; }
 </style>
 <script>

	function waitToAddSpeech() {
		if (window.cubesSpeakMoves || !window.speechSynthesis) return;
		if (window.CubeAnimation) {
			window.cubesSpeakMoves = true;
				addSpeechToCubeAnimations();
			} else {
				setTimeout(setUpCubes, 300);
			}
	}
	
	function setUpCubes() {
		if (!document.querySelector('script.roofpigscript')) {
			var script = document.createElement('script');
			script.setAttribute('src', '/blog/2023/02/using-org-babel-to-learn-rubik-s-cube-algorithms/roofpig_and_three.min.js');
			script.classList.add('roofpigscript');
			document.head.appendChild(script);
			waitToAddSpeech();
		}
	}
	
	function addSpeechToCubeAnimations() {
   if (!window.CubeAnimation || !window.CubeAnimation['by_id'] || !window.CubeAnimation['by_id'][1]) return;
   var cachedFunc = Object.getPrototypeOf(CubeAnimation['by_id'][1].dom).alg_changed;
	 Object.getPrototypeOf(CubeAnimation['by_id'][1].dom).alg_changed = function() {
		 if (arguments[4].past.length > lastNoted.length) {
			 let moves = arguments[4].past.split(' ');
			 let lastMove = moves[moves.length - 1];
			 // is it lower-case? speak lowercase explicitly
			 if (lastMove.match(/[rludbf]/)) {
				 lastMove = 'lower ' + lastMove;
			 } else { // avoid awkward-sounding moves like "capital R"
				 lastMove = lastMove.toLowerCase();
       }
       lastMove = lastMove.replace(/'/, ' prime');
			window.speechSynthesis.speak(new SpeechSynthesisUtterance(lastMove));
		 } else {
			 console.log('going backwards');
		 }
		 lastNoted = arguments[4].past;
		 return cachedFunc.apply(this, arguments);
	 }
 }
 ROOFPIG_CONF_F2L = "solved=U*|hover=none|colored=U-|flags=canvas,showalg|speed=1000";
 ROOFPIG_CONF_PLL = "solved=U-|hover=near|colored=U*|flags=canvas,showalg|speed=1000";
 var lastNoted = '';
window.addEventListener('load', setUpCubes);
</script>
<div><a href="https://sachachua.com/blog/2023/02/using-rubik-el-to-make-svg-last-layer-diagrams-from-algorithms/index.org">View org source for this post</a></div><p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2023%2F02%2Fusing-rubik-el-to-make-svg-last-layer-diagrams-from-algorithms%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></content>
		</entry><entry>
		<title type="html">Supporting A+'s cubing journey so far</title>
		<link rel="alternate" type="text/html" href="https://sachachua.com/blog/2023/02/supporting-a-s-cubing-journey-so-far/"/>
		<author><name><![CDATA[Sacha Chua]]></name></author>
		<updated>2023-02-09T16:34:41Z</updated>
    <published>2023-02-09T16:34:41Z</published>
    <category term="cubing" />
		<id>https://sachachua.com/blog/2023/02/supporting-a-s-cubing-journey-so-far/</id>
		<content type="html"><![CDATA[<p>
When I was in high school, I had friends who cubed, but I didn't get
into it much myself. I did keep a Pyraminx around as a reminder of
those days, though. In September 2021, A+ got curious about the
Pyraminx, so we decided to order a bunch of 3x3 and 2x2 cubes. We
printed out the 2x2 tutorial and helped her learn the notation. The
2x2 cube skips all the middle sections, so it's a good way to
practise. Here's how her learning journey has gone so far.
</p>

<div id="outline-container-org21a2e45" class="outline-2">
<h3 id="org21a2e45">2x2: first layer</h3>
<div class="outline-text-2" id="text-org21a2e45">
<p>
She learned to keep the white squares on the bottom, then line up the
next piece with a white square on top of the space she wanted it to go
down and repeat the 4-move sequence (<code>R U R' U'</code>) until it was in the
right place and facing the right way. She learned the 4-move sequence
by doing it slowly while we showed her on a different cube, and then
she was able to practise it on her own easily, with the quick pay-off
of being able to complete a layer.
</p>
</div>
</div>

<div id="outline-container-orgf7c0001" class="outline-2">
<h3 id="orgf7c0001">2x2: yellow squares in the second layer</h3>
<div class="outline-text-2" id="text-orgf7c0001">
<p>
The next step is to get all the yellow pieces to face up. For this, I
think we blended the 2x2 guide from the actual <a href="https://rubiks.com/en-US/solve-it">Rubik's</a> company with a
print-out that showed the different ways to hold it. She liked that
because it was easy to solve by repeating just one sequence (fish: <code>R
U R' U R U2 R'</code> - also known as Sune). The solution guide suggested
the chant "Up, Over, Down, Over, Up, Over, Over, Down," but I think
she quickly moved to just knowing it in her fingers. (Now that I'm
looking things up for this post, I've found a nice story about a
knight and a dragon in <a href="https://www.youtube.com/watch?v=jDLu0UJh8Z8">Rubik's Cube 2x2 Storytelling Method for Kids</a>.)
</p>
</div>
</div>

<div id="outline-container-orgd810388" class="outline-2">
<h3 id="orgd810388">Jumping to the 3x3: fish</h3>
<div class="outline-text-2" id="text-orgd810388">
<p>
The fish algorithm is also very useful for solving 3x3s (and it looks
more like a fish there), so around this time, she wanted me to make
the fish pattern for her so that she could "solve" it. One time we did
this more than a hundred times in a row. Here it really helped to have
several cubes, so I could set up one cube while she solved another.
</p>
</div>
</div>

<div id="outline-container-orgd655e41" class="outline-2">
<h3 id="orgd655e41">2x2: Run to me fast</h3>
<div class="outline-text-2" id="text-orgd655e41">
<p>
Back to the 2x2. She wanted to be able to solve the whole thing
herself, so she memorized the "Run to me fast" sequence from the
Rubik's cube 2x2 solution guide.
</p>

<ul class="org-ul">
<li>R'  Run to me</li>
<li>F   Fast</li>
<li>R'  Run to me</li>
<li>B2  Back back</li>
<li>R   Run away</li>
<li>F'  Fast away</li>
<li>R'  Run to me</li>
<li>B2  Back back</li>
</ul>

<p>
That was, again, an algorithm that could easily be translated to the
3x3 cube to get the corners of the last layer all matched up.
</p>
</div>
</div>

<div id="outline-container-orgc92b0d7" class="outline-2">
<h3 id="orgc92b0d7">3x3: swapping the yellow middles</h3>
<div class="outline-text-2" id="text-orgc92b0d7">
<p>
The last thing A+ needed to solve the 3x3 cube starting from the fish
was to be able to swap the last unsolved pieces. She memorized the algorithm
from the Rubik's solution guide for the 3x3 (<code>F2 U L R' F2 L' R U
F2</code>), probably by thinking of the pattern of bringing both sides down
and up.
</p>

<p>
Then she started snatching partially-solved cubes out of our hands as
soon as she recognized something she could handle. Again it helped to
have several cubes on the go, and it helped that both W- and I were
learning along with her. (I think I was learning more about
patience than about cubing&#x2026;)
</p>
</div>
</div>

<div id="outline-container-orgb28d8e4" class="outline-2">
<h3 id="orgb28d8e4">3x3: working backwards from there</h3>
<div class="outline-text-2" id="text-orgb28d8e4">
<p>
Eventually she wanted to be able to solve more of the cube for
herself. She learned how to put the yellow pieces into a cross, then
learned how to put the middle edges into the second layer. Somewhere
along the way, she started using the daisy method to make the white
cross on the first layer, using her old favourite <code>R U R' U'</code> to put
the white corners into the right places.
</p>
</div>
</div>

<div id="outline-container-org178e78b" class="outline-2">
<h3 id="org178e78b">Going forward</h3>
<div class="outline-text-2" id="text-org178e78b">
<p>
She's been improving on her own. After watching videos about doing the
white cross on the bottom, she stopped using the daisy and started
doing the white cross on the bottom too. I think she's been working on
F2L, pairing up the corner and the middle before inserting it. I've
been learning some PLL algorithms, so she's been working on them too.
Tucked under a blanket, we swap cubes back and forth, setting up and
solving the same perms. Sometimes it's difficult for her to choose the
right speed to practise something. She gets the algorithm right when
she does it slowly, but mixes up a step if she tries to move quickly
too soon, and then she gets frustrated. But she's learning to slow
down and try again, and that's fantastic.
</p>

<p>
She doesn't usually confuse <code>L</code> and <code>R</code>, but she still asks me about
clockwise and counterclockwise sometimes. I made it easier to <a href="https://sachachua.com/blog/2023/02/using-org-babel-to-learn-rubik-s-cube-algorithms/">add
algorithm animations</a> to the little website I keep for her on our
network. Hmm, maybe I could try some more mnemonics to help her
remember which way clockwise is using fingertricks and letter sounds:
Four balls (front right, back left)? Leave them to me (left) right
away: up right, down low (left).
</p>

<p>
It's fun to learn with her. I've been challenging myself to plan more
of my crosses and pairs by closing my eyes in between sets of moves.
She sometimes does the same, calling it "part-blind." She shows me
fingertricks. She's faster than I am and will probably reach the
20-second PB or 30-second average before I reach the 30-second PB or
40-second average. I think there's something really interesting about
having your hands learn something that your brain might still be a
little slow to grasp. I'm glad this tickled her curiosity (and ours).
</p>

<p>
</p><div class="sketch-full"><a class="photoswipe" href="https://sketches.sachachua.com/filename/2023-02-08-02%20Supporting%20the%20kid's%20cubing%20journey%20so%20far%20%23cubing.png" data-src="https://sketches.sachachua.com/static/2023-02-08-02%20Supporting%20the%20kid's%20cubing%20journey%20so%20far%20%23cubing.png" data-title="2023-02-08-02 Supporting the kid's cubing journey so far #cubing.png" data-w="2808" data-h="3744"><picture>
      <img src="https://sketches.sachachua.com/static/2023-02-08-02%20Supporting%20the%20kid's%20cubing%20journey%20so%20far%20%23cubing.png" width="2808" height="3744" alt="2023-02-08-02 Supporting the kid's cubing journey so far #cubing.png" loading="lazy" style="max-height: 90vw; height: auto; width: auto" decoding="async">
      <figcaption>2023-02-08-02 Supporting the kid's cubing journey so far #cubing.png</figcaption>
    </picture></a></div>
<p></p>
</div>
</div>
<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2023%2F02%2Fsupporting-a-s-cubing-journey-so-far%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></content>
		</entry><entry>
		<title type="html">Using Org Babel to learn Rubik's cube algorithms</title>
		<link rel="alternate" type="text/html" href="https://sachachua.com/blog/2023/02/using-org-babel-to-learn-rubik-s-cube-algorithms/"/>
		<author><name><![CDATA[Sacha Chua]]></name></author>
		<updated>2023-02-02T14:43:39Z</updated>
    <published>2023-02-02T14:43:39Z</published>
    <category term="emacs" />
<category term="cubing" />
<category term="org" />
		<id>https://sachachua.com/blog/2023/02/using-org-babel-to-learn-rubik-s-cube-algorithms/</id>
		<content type="html"><![CDATA[<p>
A+ has started learning Rubik's cube algorithms for permutation of the
last layer (PLL) algorithms for the Rubik's cube. To help her focus on
just a few at a time instead of getting distracted by the long list in
the <a href="https://www.cubeskills.com/uploads/pdf/tutorials/pll-algorithms.pdf">Cubeskills PLL PDF</a>, I made a page that listed the algorithms that
she was working on so that I could export it with ox-hugo to the
mini-site I made for her interests.
</p>

<p>
She sometimes gets a little confused about clockwise and
counter-clockwise, so I used <a href="https://github.com/larspetrus/Roofpig">Roofpig</a> to add an animated view of the
algorithm that she can step through. I wanted to make it easier for
her to follow the algorithm without constantly moving her hands from
the cube to the tablet or looking up and down all the time, but I
didn't want to recompile the source just yet. I used the
<a href="https://sachachua.com/blog/2023/02/using-org-babel-to-learn-rubik-s-cube-algorithms/roofpig_and_three.min.js">roofpig_and_three.min.js</a> file and hacked speech synthesis into it by
modifying the object prototype. For example, here's the Org source for
adding a Jb perm:
</p>

<div class="org-src-container">
<pre class="src src-org"><span class="org-org-block-begin-line">#+begin_export html</span>
<span class="org-org-block">&lt;</span><span class="org-org-block"><span class="org-function-name">div</span></span><span class="org-org-block"> </span><span class="org-org-block"><span class="org-variable-name">class</span></span><span class="org-org-block">=</span><span class="org-org-block"><span class="org-string">"roofpig"</span></span><span class="org-org-block"> </span><span class="org-org-block"><span class="org-variable-name">data-config</span></span><span class="org-org-block">=</span><span class="org-org-block"><span class="org-string">"base=PLL|alg=R U R' F' R U R' U' R' F R2 U' R' U'"</span></span><span class="org-org-block">&gt;&lt;/</span><span class="org-org-block"><span class="org-function-name">div</span></span><span class="org-org-block">&gt;</span>
<span class="org-org-block-end-line">#+end_export</span>
</pre>
</div>

<p>
and here's what it looks like. Might only look nice on my website, and I added speech synthesis so you may want to mute it if you need to be quiet.
</p>

<div class="roofpig" data-config="base=PLL|alg=R U R' F' R U R' U' R' F R2 U' R' U'"></div>

<details><summary>Code for setting up Roofpig with speech synthesis</summary><div class="org-src-container">
<pre class="src src-web">&lt;!&#45;&#45;  -*- mode: web -*- &#45;&#45;&gt;
&lt;style&gt;
 <span class="org-web-mode-css-selector-class">.roofpig</span> { <span class="org-web-mode-css-property-name">max-width:</span> 400px; <span class="org-web-mode-css-property-name">margin-bottom:</span> 80px; }
 &lt;/style&gt;
 &lt;script&gt;

  <span class="org-web-mode-keyword">function</span> <span class="org-web-mode-function-name">waitToAddSpeech</span>() {
    <span class="org-web-mode-keyword">if</span> (window.cubesSpeakMoves || !window.speechSynthesis) <span class="org-web-mode-keyword">return</span>;
    <span class="org-web-mode-keyword">if</span> (window.CubeAnimation) {
      window.cubesSpeakMoves = <span class="org-web-mode-constant">true</span>;
        <span class="org-web-mode-function-call">addSpeechToCubeAnimations</span>();
      } <span class="org-web-mode-keyword">else</span> {
        <span class="org-web-mode-function-call">setTimeout</span>(setUpCubes, 300);
      }
  }
  
  <span class="org-web-mode-keyword">function</span> <span class="org-web-mode-function-name">setUpCubes</span>() {
    <span class="org-web-mode-keyword">if</span> (!document.<span class="org-web-mode-function-call">querySelector</span>('script.roofpig')) {
      <span class="org-web-mode-keyword">var</span> <span class="org-web-mode-variable-name">script</span> = document.<span class="org-web-mode-function-call">createElement</span>('script');
      script.<span class="org-web-mode-function-call">setAttribute</span>('src', '/blog/2023/02/using-org-babel-to-learn-rubik-s-cube-algorithms/roofpig_and_three.min.js');
      script.classList.<span class="org-web-mode-function-call">add</span>('roofpig');
      document.head.<span class="org-web-mode-function-call">appendChild</span>(script);
      <span class="org-web-mode-function-call">waitToAddSpeech</span>();
    }
  }
  
  <span class="org-web-mode-keyword">function</span> <span class="org-web-mode-function-name">addSpeechToCubeAnimations</span>() {
   <span class="org-web-mode-keyword">if</span> (!window.CubeAnimation || !window.CubeAnimation['by_id'] || !window.CubeAnimation['by_id'][1]) <span class="org-web-mode-keyword">return</span>;
   <span class="org-web-mode-keyword">var</span> <span class="org-web-mode-variable-name">cachedFunc</span> = Object.<span class="org-web-mode-function-call">getPrototypeOf</span>(CubeAnimation['by_id'][1].dom).alg_changed;
   Object.<span class="org-web-mode-function-call">getPrototypeOf</span>(CubeAnimation['by_id'][1].dom).alg_changed = <span class="org-web-mode-keyword">function</span>() {
     <span class="org-web-mode-keyword">if</span> (<span class="org-web-mode-constant">arguments</span>[4].past.length &gt; lastNoted.length) {
       <span class="org-web-mode-keyword">let</span> <span class="org-web-mode-variable-name">moves</span> = <span class="org-web-mode-constant">arguments</span>[4].past.<span class="org-web-mode-function-call">split</span>(' ');
       <span class="org-web-mode-keyword">let</span> <span class="org-web-mode-variable-name">lastMove</span> = moves[moves.length - 1];
       // is it lower-case? speak lowercase explicitly
       <span class="org-web-mode-keyword">if</span> (lastMove.<span class="org-web-mode-function-call">match</span>(/[rludbf]/)) {
         lastMove = 'lower ' + lastMove;
       } <span class="org-web-mode-keyword">else</span> { // avoid awkward-sounding moves like "capital R"
         lastMove = lastMove.<span class="org-web-mode-function-call">toLowerCase</span>();
       }
       lastMove = lastMove.<span class="org-web-mode-function-call">replace</span>(/'/, ' prime');
      window.speechSynthesis.<span class="org-web-mode-function-call">speak</span>(<span class="org-web-mode-keyword">new</span> <span class="org-web-mode-type">SpeechSynthesisUtterance</span>(lastMove));
     } <span class="org-web-mode-keyword">else</span> {
       console.<span class="org-web-mode-function-call">log</span>('going backwards');
     }
     lastNoted = <span class="org-web-mode-constant">arguments</span>[4].past;
     <span class="org-web-mode-keyword">return</span> cachedFunc.<span class="org-web-mode-function-call">apply</span>(<span class="org-web-mode-constant">this</span>, <span class="org-web-mode-constant">arguments</span>);
   }
 }
 ROOFPIG_CONF_F2L = "solved=U*|hover=none|colored=U-|flags=canvas,showalg|speed=1000";
 ROOFPIG_CONF_PLL = "solved=U-|hover=near|colored=U*|flags=canvas,showalg|speed=1000";
 <span class="org-web-mode-keyword">var</span> <span class="org-web-mode-variable-name">lastNoted</span> = '';
window.<span class="org-web-mode-function-call">addEventListener</span>('load', setUpCubes);
&lt;/script&gt;
</pre>
</div></details>


<style>
 .roofpig { max-width: 400px; margin-bottom: 80px; }
 </style>
 <script>

	function waitToAddSpeech() {
		if (window.cubesSpeakMoves || !window.speechSynthesis) return;
		if (window.CubeAnimation) {
			window.cubesSpeakMoves = true;
				addSpeechToCubeAnimations();
			} else {
				setTimeout(setUpCubes, 300);
			}
	}
	
	function setUpCubes() {
		if (!document.querySelector('script.roofpig')) {
			var script = document.createElement('script');
			script.setAttribute('src', '/blog/2023/02/using-org-babel-to-learn-rubik-s-cube-algorithms/roofpig_and_three.min.js');
			script.classList.add('roofpig');
			document.head.appendChild(script);
			waitToAddSpeech();
		}
	}
	
	function addSpeechToCubeAnimations() {
   if (!window.CubeAnimation || !window.CubeAnimation['by_id'] || !window.CubeAnimation['by_id'][1]) return;
   var cachedFunc = Object.getPrototypeOf(CubeAnimation['by_id'][1].dom).alg_changed;
	 Object.getPrototypeOf(CubeAnimation['by_id'][1].dom).alg_changed = function() {
		 if (arguments[4].past.length > lastNoted.length) {
			 let moves = arguments[4].past.split(' ');
			 let lastMove = moves[moves.length - 1];
			 // is it lower-case? speak lowercase explicitly
			 if (lastMove.match(/[rludbf]/)) {
				 lastMove = 'lower ' + lastMove;
			 } else { // avoid awkward-sounding moves like "capital R"
				 lastMove = lastMove.toLowerCase();
       }
       lastMove = lastMove.replace(/'/, ' prime');
			window.speechSynthesis.speak(new SpeechSynthesisUtterance(lastMove));
		 } else {
			 console.log('going backwards');
		 }
		 lastNoted = arguments[4].past;
		 return cachedFunc.apply(this, arguments);
	 }
 }
 ROOFPIG_CONF_F2L = "solved=U*|hover=none|colored=U-|flags=canvas,showalg|speed=1000";
 ROOFPIG_CONF_PLL = "solved=U-|hover=near|colored=U*|flags=canvas,showalg|speed=1000";
 var lastNoted = '';
window.addEventListener('load', setUpCubes);
</script>

<p>
I also wanted to include diagrams to make it easier for her to choose
the right algorithm and position the cube the right way at the
beginning, but I didn't want to fuss around with lots of screenshots
and little files. It turns out you can define <a href="https://thenewcode.com/1068/Making-Arrows-in-SVG">arrows in SVG</a> pretty
easily, so I wrote some Emacs Lisp functions to generate those types
of diagrams. First I started with just the arrows.
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp">(my-cubing-last-layer-arrows <span class="org-highlight-quoted-quote">'</span>((2 8 t) (5 7 t)))
</pre>
</div>


<figure id="org3a3fccc">
<img src="https://sachachua.com/blog/2023/02/using-org-babel-to-learn-rubik-s-cube-algorithms/arrows.svg" alt="arrows.svg" class="org-svg">

</figure>

<p>
For practising recognition, I wanted to also include the colors on top and on the sides:
</p>

<div class="org-src-container">
<pre class="src src-emacs-lisp">(my-cubing-last-layer-with-sides <span class="org-string">"OOGRROGGRBBB"</span> <span class="org-string">"YYYYYYYYY"</span> <span class="org-highlight-quoted-quote">'</span>((2 8 t) (5 7 t)))
</pre>
</div>


<figure id="orgb911ca0">
<img src="https://sachachua.com/blog/2023/02/using-org-babel-to-learn-rubik-s-cube-algorithms/colors.svg" alt="colors.svg" class="org-svg">

</figure>

<p>
</p><details><summary>Emacs Lisp functions for cubing diagrams</summary><div class="org-src-container">
<pre class="src src-emacs-lisp"><span class="org-comment-delimiter">;; </span><span class="org-comment">Start of cubing code</span>
(<span class="org-keyword">defun</span> <span class="org-function-name">my-cubing-pos</span> (size n i)
  (list
   (* (/ size n) (% i n))
   (* (/ size n) (/ i n))))
  
(<span class="org-keyword">defun</span> <span class="org-function-name">my-cubing-last-layer-arrows</span> (arrows)
  <span class="org-doc">"Draw ARROWS.</span>
<span class="org-doc">Arrows are defined as a list of lists of the form</span>
<span class="org-doc">((from to) (from to t) ...). Ex: '(my-cubing-last-layer-arrows '((3 1 t) (2 8 t)))</span>
<span class="org-doc">Cells are numbered from left to right, top to bottom, with the top left box being 0.</span>
<span class="org-doc">"</span>
  (<span class="org-keyword">let*</span> ((size 99)
         (n 3)
         (arrow-color <span class="org-string">"#000"</span>)
         (svg (svg-create size size)))
    (svg&#45;&#45;append
     svg
     (dom-node
      <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">defs</span>
      nil
      (dom-node
       <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">marker</span>
       <span class="org-highlight-quoted-quote">'</span>((id . <span class="org-string">"arrowhead"</span>)
         (markerWidth . <span class="org-string">"10"</span>)
         (markerHeight . <span class="org-string">"7"</span>)
         (refX . <span class="org-string">"0"</span>)
         (refY . <span class="org-string">"3.5"</span>)
         (orient . <span class="org-string">"auto-start-reverse"</span>))
       (dom-node
        <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">polygon</span>
        <span class="org-highlight-quoted-quote">`</span>((fill . ,arrow-color)
          (points . <span class="org-string">"0 0, 4 3.5, 0 7"</span>)))
       )))
    (<span class="org-keyword">dotimes</span> (i (* n n))
      (<span class="org-keyword">let</span> ((pos (my-cubing-pos size n i)))
        (svg-rectangle
         svg
         (car pos)
         (cadr pos)
         (/ size n)
         (/ size n)
         <span class="org-builtin">:fill</span> <span class="org-string">"#fff"</span>
         <span class="org-builtin">:stroke-width</span> 1
         <span class="org-builtin">:stroke</span> <span class="org-string">"#666"</span>)))
    (<span class="org-keyword">dolist</span> (arrow arrows)
      (<span class="org-keyword">let</span> ((from (car arrow))
            (to (cadr arrow)))
        (apply <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">svg-line</span>
               (append
                (list svg)
                (mapcar (<span class="org-keyword">lambda</span> (o) (+ o (/ size (* 2 n))))
                        (my-cubing-pos size n from))
                (mapcar (<span class="org-keyword">lambda</span> (o) (+ o (/ size (* 2 n))))
                        (my-cubing-pos size n to))
                (list
                 <span class="org-builtin">:stroke-width</span> 2
                 <span class="org-builtin">:stroke</span> arrow-color
                 <span class="org-builtin">:marker-start</span> (<span class="org-keyword">if</span> (elt arrow 2) <span class="org-string">"url(#arrowhead)"</span>)
                 <span class="org-builtin">:marker-end</span> <span class="org-string">"url(#arrowhead)"</span>)))))
    (<span class="org-keyword">with-temp-buffer</span>
      (svg-print svg)
      (buffer-string))))

(<span class="org-keyword">defvar</span> <span class="org-variable-name">my-cubing-colors</span> <span class="org-highlight-quoted-quote">'</span>((?R  . <span class="org-string">"#ff0000"</span>)
                           (?G  . <span class="org-string">"#00ff00"</span>)
                           (?B  . <span class="org-string">"#0000ff"</span>)
                           (?O  . <span class="org-string">"#ed7117"</span>)
                           (?Y  . <span class="org-string">"#ffff00"</span>)
                           (?W  . <span class="org-string">"#ffffff"</span>)
                           (?\? . <span class="org-string">"#666666"</span>)))

(<span class="org-keyword">defun</span> <span class="org-function-name">my-cubing-last-layer-with-sides</span> (sides top arrows)
  <span class="org-doc">"Draw a diagram of the top of the cube.</span>
<span class="org-doc">The style is similar to https://www.cubeskills.com/uploads/pdf/tutorials/pll-algorithms.pdf .</span>
<span class="org-doc">SIDES is a string specifying colors going clockwise from the back-left side.</span>
<span class="org-doc">TOP is a string specifying colors going from left to right, top to bottom.</span>
<span class="org-doc">Arrows are defined as a list of lists of the form ((from to) (from to t) ...).</span>
<span class="org-doc">Cells are numbered from left to right, top to bottom, with the top left box being 0.</span>
<span class="org-doc">Ex: (my-cubing-last-layer-with-sides \"ORRBOOGGGRBB\" \"YYYYYYYYY\" '((3 1 t) (2 8 t)))</span>
<span class="org-doc">"</span>
  (<span class="org-keyword">let*</span> ((size 99)
         (n 3)
         (side-size 10)
         (cell-size (/ (- size (* 2 side-size)) n))
         (arrow-color <span class="org-string">"#000"</span>)
         (svg (svg-create size size)))
    (svg&#45;&#45;append
     svg
     (dom-node
      <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">defs</span>
      nil
      (dom-node
       <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">marker</span>
       <span class="org-highlight-quoted-quote">'</span>((id . <span class="org-string">"arrowhead"</span>)
         (markerWidth . <span class="org-string">"10"</span>)
         (markerHeight . <span class="org-string">"7"</span>)
         (refX . <span class="org-string">"0"</span>)
         (refY . <span class="org-string">"3.5"</span>)
         (orient . <span class="org-string">"auto-start-reverse"</span>))
       (dom-node
        <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">polygon</span>
        <span class="org-highlight-quoted-quote">`</span>((fill . ,arrow-color)
          (points . <span class="org-string">"0 0, 4 3.5, 0 7"</span>))))))
    <span class="org-comment-delimiter">;; </span><span class="org-comment">Draw the sides. It's a string of colors going clockwise from back left</span>
    (<span class="org-keyword">when</span> sides
      (<span class="org-keyword">dotimes</span> (i (* n 4))
        (apply <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">svg-rectangle</span>
               (append
                (list svg)
                (<span class="org-keyword">pcase</span> (/ i n)
                  (0 (list (+ (* (% i n) cell-size) side-size)
                           0
                           cell-size
                           side-size))
                  (1 (list (+ side-size (* n cell-size))
                           (+ (* (% i n) cell-size) side-size)
                           side-size
                           cell-size))
                  (2 (list (+ (* (- n (% i n) 1) cell-size) side-size)
                           (+ (* n cell-size) side-size)
                           cell-size
                           side-size))
                  (3 (list 0
                           (+ (* (- n (% i n) 1) cell-size) side-size)
                           side-size
                           cell-size)))
                (list
                 <span class="org-builtin">:stroke-width</span> 1
                 <span class="org-builtin">:stroke</span> <span class="org-string">"#666"</span>
                 <span class="org-builtin">:fill</span> (assoc-default (elt sides i)
                                      my-cubing-colors
                                      <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">eq</span>
                                      (assoc-default ?\? my-cubing-colors)))))))
    <span class="org-comment-delimiter">;; </span><span class="org-comment">Draw the top face specified by a string of colors going from left to right, top to bottom</span>
    (<span class="org-keyword">dotimes</span> (i (* n n))
      (<span class="org-keyword">let</span> ((pos (my-cubing-pos (* cell-size n) n i)))
        (svg-rectangle
         svg
         (+ side-size (car pos))
         (+ side-size (cadr pos))
         cell-size
         cell-size
         <span class="org-builtin">:fill</span> (<span class="org-keyword">if</span> top
                   (assoc-default (elt top i) my-cubing-colors
                                  <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">eq</span>
                                  (assoc-default ?\? my-cubing-colors))
                 (assoc-default ?\? my-cubing-colors))
         <span class="org-builtin">:stroke-width</span> 1
         <span class="org-builtin">:stroke</span> <span class="org-string">"#666"</span>)))
    <span class="org-comment-delimiter">;; </span><span class="org-comment">Draw the arrows</span>
    (<span class="org-keyword">dolist</span> (arrow arrows)
      (<span class="org-keyword">let</span> ((from (car arrow))
            (to (cadr arrow)))                  
        (apply <span class="org-highlight-quoted-quote">'</span><span class="org-highlight-quoted-symbol">svg-line</span>
               (append
                (list svg)
                (mapcar (<span class="org-keyword">lambda</span> (o) (+ side-size o (/ cell-size 2)))
                        (my-cubing-pos (* n cell-size) n from))
                (mapcar (<span class="org-keyword">lambda</span> (o) (+ side-size o (/ cell-size 2)))
                        (my-cubing-pos (* n cell-size) n to))
                (list
                 <span class="org-builtin">:stroke-width</span> 2
                 <span class="org-builtin">:stroke</span> arrow-color
                 <span class="org-builtin">:opacity</span> 0.5
                 <span class="org-builtin">:marker-start</span> (<span class="org-keyword">if</span> (elt arrow 2) <span class="org-string">"url(#arrowhead)"</span>)
                 <span class="org-builtin">:marker-end</span> <span class="org-string">"url(#arrowhead)"</span>)))))
    (<span class="org-keyword">with-temp-buffer</span>
      (svg-print svg)
      (buffer-string))))
<span class="org-comment-delimiter">;; </span><span class="org-comment">end of cubing code</span>
</pre>
</div></details>

<p></p>

<p>
I'll probably need to tweak the arrows when we eventually get to the G
perms, but we're still a long way off. And it would probably be pretty
cool to be able to generate the colours by going backwards from the
algorithm, maybe building on top of <a href="https://codeberg.org/akib/emacs-cube">emacs-cube</a>, so that I can write my
own notes about recognizing the in-between steps and recovering from
the typical mistakes we make. (That wasn't around the last time I
wrote about Emacs and <a href="https://sachachua.com/blog/2021/11/turns-out-the-rubik-s-cube-is-just-right-for-this-stage-with-a/">cubing</a>. Thanks to Akib for making and sharing
it!) I'm curious about <a href="https://tex.stackexchange.com/questions/459254/easy-way-to-generate-rubiks-cube-diagrams">this LaTeX approach</a>, too, but that can wait for
another day.
</p>
<div><a href="https://sachachua.com/blog/2023/02/using-org-babel-to-learn-rubik-s-cube-algorithms/index.org">View org source for this post</a></div><p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2023%2F02%2Fusing-org-babel-to-learn-rubik-s-cube-algorithms%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></content>
		</entry><entry>
		<title type="html">Turns out the Rubik's cube is just right for this stage with A-</title>
		<link rel="alternate" type="text/html" href="https://sachachua.com/blog/2021/11/turns-out-the-rubik-s-cube-is-just-right-for-this-stage-with-a/"/>
		<author><name><![CDATA[Sacha Chua]]></name></author>
		<updated>2021-11-17T00:00:00Z</updated>
    <published>2021-11-17T00:00:00Z</published>
    <category term="parenting" />
<category term="fun" />
<category term="cubing" />
		<id>https://sachachua.com/blog/2021/11/turns-out-the-rubik-s-cube-is-just-right-for-this-stage-with-a/</id>
		<content type="html"><![CDATA[<p>
I spend a lot of time waiting for A-. Sometimes I'm waiting for her to
finish reading a book or watching a video. Sometimes it takes her
forever to get to bed. She can sometimes amuse herself independently,
but she often still wants me around somewhere in the room. Someday she
won't, so in the meantime, I wait. I can't be on my phone or laptop
during times like that, because then she'll want screentime too.
Sometimes I tidy, sometimes I read, sometimes I write.
</p>

<p>
It turns out that learning to solve the Rubik's cube is an interest
that slots neatly into my life with A-. We picked it up recently
because A- was interested in my old Pyraminx.
</p>

<p>
Our order from <a href="https://www.cubingoutloud.com/">Cubing Out Loud</a> turned out to be a pretty good
introduction to the world of speedcubing:
</p>

<ul class="org-ul">
<li>a MoYu RS3 M 2020, a magnetized 3x3x3 cube for $10 CAD</li>
<li>a YuXin Little Magic 3x3x3 M, another magnetized 3x3x3 cube for $9 CAD</li>
<li>a YJ MGC 2x2x2 M, a magnetized 2x2x2 cube for $11 CAD</li>
<li>and some lubricant</li>
</ul>

<p>
The speed cubes were way smoother than the Rubik's cubes I remember
from high school and university. The 2x2x2 cube was great for helping
A- practise simple algorithms and get that feeling of success. She
quickly graduated to the 3x3x3 cubes. She loves solving it from the
fish position, so W- and I solve the first two layers, and then she
solves it from there. I was pleasantly surprised at how quickly she
picked up the beginner algorithms that we showed her, and she took
great delight in learning finger tricks and being able to do the Sune
move in three seconds. I can do the Anti-Sune just about as fast as
she can do the Sune, so we trade cubes back and forth. Sometimes I mix
things up so that she has to permute the last layer, too. She's
gradually branching out to more algorithms, and will sometimes even
take on solving it from a full scramble.
</p>

<p>
Cubing seems to be a good way for her to practise distinguishing left
from right, clockwise from counter-clockwise. We talk about averages,
minimums, and moves per second. She likes taking apart our cubes,
tweaked the tension, and lubing them. (Reassembling them is a job for
grown-ups, apparently.) She likes playing around with different
patterns. It spread into her pretend play too. She loves watching
JPerm and parroting his lines.
</p>

<p>
For my part, I enjoy slowly learning different algorithms and feeling
things start to click. I can usually solve the 3x3 in under two
minutes now (nothing remarkable; most beginners get there), and have
lately been averaging around 1:30. I'm getting the hang of solving
colour-neutral crosses by moving edges around and ignoring centers,
and of solving the first two layers together. I like practising
algorithms while keeping an eye on her at the playground. I'm getting
better at smiling even when A- snatches the partially-solved cube I
was working on with the timer running. I'm not aiming for any records,
anyway.
</p>

<p>
Since W- has gotten into cubing as well, we have determined that we
need more cubes. Also, to save our phones from A-'s rather
enthusiastic timer use, a StackMat timer and a mat are probably a good
idea. <a href="https://speedcubeshop.com/">Speed Cube Shop</a> had a wider selection than Cubing Out Loud, so
we ordered a few cubes and accessories from there. She insisted on
getting a Gan cube with some of her savings. Hey, at least these
highly-engineered bits of plastic generally stay in one piece, don't
get scattered all over the floor, don't need to be sorted into various
bins, and don't get stepped on. (I'm kidding, LEGO, we still like
you.)
</p>

<p>
In terms of Android apps, I like <a href="https://play.google.com/store/apps/details?id=com.cube.nanotimer&amp;hl=en&amp;gl=US">Nano Timer</a>. It's free and allows me
to keep times in different categories, like a regular solve, A-
starting from the fish, or co-op. There's even a multi-step timer for
breaking down things like CFOP. A- likes <a href="https://play.google.com/store/apps/details?id=air.tw.url.omega.FingerTimer&amp;hl=en&amp;gl=US">Finger Timer</a> because it looks
like a StackMat timer.
</p>

<p>
Naturally, I'm getting the urge to do something about Rubik's cubes
and Emacs. A timer that will let me quickly reassign my current time
from "Regular 3x3 solve" to "Solved until A- grabbed the fish"? (It'll
have to work on my phone - maybe Termux or SSH, or a web-based
approach&#x2026;) An Org Babel block type for visualizing cubes and moves
so that I can make my own notes and blog posts? An SVG version of that
<a href="https://github.com/Kurvivor19/rubik-mode">text-based Rubik's cube</a> that someone wrote for Emacs? A scramble
generator that lets me pick the type of scramble I want and then uses
the Kociemba algorithm to generate the steps for scrambling it?
Anyway, it'll have to wait until I get a few things off my plate, like
EmacsConf and the usual year-end paperwork.
</p>

<p>
In the meantime, I have things to learn while I wait. I think I'd like
to get to the point of being able to do the cross blind. I'm also
working on memorizing the rest of 4LLL, and then full OLL/PLL after
that.. Anyway, so that's what we've been up to in the evenings while
waiting for A- to go to bed.
</p>
<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2021%2F11%2Fturns-out-the-rubik-s-cube-is-just-right-for-this-stage-with-a%2F&body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p>]]></content>
		</entry>
</feed>