Hello, everyone, and welcome to Speedcubing in Emacs.
First of all, a little bit about myself.
My name is Vasilij Schneidermann. Online, I go by wasamasa.
I'm 31 years old. I work in information security,
and I do consulting and hacking
and stuff like figuring out
how to break into other people's computers
and how to secure their systems basically.
You can reach me by email.
I do have a self-hosted code repository thingy going on.
I have a blog, and you can find me
in some other places online, like IRC for example.
So about the talk itself,
I used to be into the Rubik's cube when I was in school.
I forgot about it, though,
because these cubes were not very good.
Recently I did find some cheap looking cube at a shop.
Did not pay terribly much for it.
It was so, so much better than my old cube,
it was unreal.
This motivated me to get back into
this really weird kind of hobby.
For this, you need to be good at producing
a truly random scramble
and timing your attempts to get any better at it.
There is, of course, existing software
to do the scrambling for you and the recording
and the timekeeping and such,
but all the good options seem to be either web or mobile,
for example the cstimer software
or the twisty-timer app on Android.
To my surprise, I did not find a single decent option
inside Emacs, so this is basically a case study
how to do better. For this, I wanted to make use of
all the cool new Emacs features that appeared,
like the SVG library; Transient,
the library used for the Magit-style interfaces;
and the recently added sqlite-mode.
And most importantly it was about having fun.
So here's a full list of prior art,
I will not go into detail about this,
but basically we have things solving
very different parts of this,
but not all of it. For example: we have several,
we have a timer. We have several solvers.
We have some scramblers.
We have some whole-cube simulators, including a 3D one.
We have something for making it easier
to enter your algorithms in the notation.
But nothing that does all of those things in one package,
which kind of surprised me.
So I present the `wca-prep` package.
So the name, I found it difficult
to come up with a good name and so I looked
and I saw, well there's this World Cube Association
that holds these competitions where you compete.
They do this for the Rubik's cube
but also a few others,
so there's like a standardized list
of events they have for this.
There is a standard notation for this
and rules and everything.
And the goal of my package is basically
to help prepare myself for such a competition
and in fact a week ago I went to my first one
which was wild, but pretty cool.
So for this reason I chose this name wca-prep,
because it helps me prepare for this kind of competition
and this limited the scope significantly,
I have a scrambler, visualization of the scramble,
timer, and statistics.
I excluded pretty much everything else I've seen.
For this reason, I only tried to focus on
some very basic puzzles I can solve comfortably,
and did not want to do anything else
that may complicate things significantly.
No other kinds of puzzles, no simulation, no solving,
no exotic events, and no specialized scrambles
that are only good for practicing specific algorithms.
So at this point the organizer should hopefully show
a small video I've prepared, a one minute video showing how
I actually use this to solve a cube and to time my solve.
Okay, so building this thing, there were several challenges.
The first one was how do I even represent
the state of a Rubik's cube.
For this there are many possible representations,
no obvious best solution.
I did not, well, what helped me was that
I did not have to programmatically solve this thing,
so I picked the easiest possible representation
which is just an array of every single facelet.
For a 3x3 cube you have 9 facelets on one side,
so times 6 sides you would have 54 elements in this array.
So with this representation, it's very simple,
but it's kind of weird to do scrambles with this.
But otherwise, it worked very, very well.
In the future, I plan to learn some group theory,
pick a better representation
and do this in a much, much more elegant way
without compromising speed too much.
Yes. Once I had the representation,
the scrambling itself should not be too hard.
For this, it's important to consider that basically
if you do a face turn
you end up swapping some facelets with other facelets,
that's the easiest way to think about this.
To determine which one goes into which one's position,
it was pretty confusing to figure this out.
For this I went through a few papers,
and I found one which suggested
to just build a cube out of paper,
number every facelet, and turn it
and keep track of which facelet moved into which position.
And programmatically, the `cl-rotatef` macro
was very, very useful for doing this kind of
in-place swapping you need for this operation.
So in the future, group theory would hopefully
make this a bit less awkward.
Here's a photo of this paper cube I made
along with a real cube. As you can see
mathematically speaking, they are the same thing,
they just look very, very different.
So the scramble algorithm itself,
I pondered how this would even be done. In the competitions,
They do this in a very, very elaborate way.
They generate a random cube,
they try to solve it, and if it's solvable
they use these solution moves
to turn into a scramble basically.
And they also make sure to canonicalize the moves,
so if you have subsequent moves that can be simplified,
they do simplify these as much as possible.
For example,
if you have two subsequent rotations in one direction,
it's turned into a different kind of rotation,
so 90 and 90 equals 180.
And the other Elisp scramblers I looked at,
they generate random moves.
Some of them do canonicalize. Not all of them.
This one tries to do the best low-fi thing,
that is, generating random moves,
canonicalizing and repeating
until enough have been generated.
For the visualization I had to figure out
something else too complicated.
For this, I tried to figure out
where every facelift would end up in the puzzle view
when you would unfold it.
And for this, I did not consider the facelet orientation.
This may be important later for some other puzzles
where you can end up with very twisted faces,
but for simple cubes, it's not a problem.
My initial prototype used colored text,
but later, I used the SVG library.
It turned out to be easy enough to use, actually.
Currently, I have hard-coded face-color mappings,
but I plan to replace this so that theming is possible.
For example, if you happen to have a cube
that does not have the same color mappings as I do,
then you should be able to fix this.
Next challenge was to build
a beautiful intuitive UI with Transient.
The reason why I chose this is
because it would be self-documenting and Magit-style,
and everyone knows how Magit works basically.
Since Transient has become part of Emacs,
there is really no reason to not try it out.
The problem was documentation is difficult to understand.
It's very abstract and high level,
and it's hard to figure out. "Okay,
I want to do something,
how am I supposed to do this?"
I did find transient-showcase, which has lots of examples,
but they don't really feel finished
and not realistic enough.
When I tried to use the package,
I got plenty of unhelpful error messages
when using it incorrectly.
I did manage to figure it out,
but I plan to find more actual examples of it,
to have an executable reference basically
and try to improve my use of it.
For the book-keeping, I used SQLite.
This is a very recent addition to Emacs,
it only appeared in the current major version.
It's still very early days.
I found some oddities, one of them turned out to be
a bug in the transaction macro.
Like basically, if you do an SQL transaction
and an error happens, then every helper I found
does a rollback on an error.
But this one did not. It actually committed on an error,
and this was very weird to figure out.
I reported a bug. Eli was nice enough to send me a patch.
We did some patch review,
and he ended up fixing it properly.
So yes, there's still a lot to be done there, and yeah,
the API is very basic.
You don't have convenience helpers
like fetch the first row or fetch the first value
or anything, but they're easy enough to write yourself.
And the biggest challenge with this bookkeeping part
was figuring out a decent schema,
like how to organize data correctly
so that it would not be awkward to manipulate.
And with this, you can finally build a package
that remembers its state properly
and don't have to run into foot guns
with Lisp-style serialization, deserialization.
So yes, that concludes it so far.
So what did I learn from this exercise?
Well, there are still plenty of packages
for Emacs to be written.
If you think everything you can think of
or you need has already been written, well, guess what?
No.
These are still plenty of specialized things
that could need your help.
These cubes do not require advanced mathematics,
contrary to what you may think.
Yes, you can apply advanced mathematics to them
if you want to, but you don't have to.
What surprised me about this is basically group theory.
I've heard of it before.
It seemed to be a meme, basically,
because it has been like mostly Haskell people
being very excited about this
and it seemed kind of, like, divorced from reality, basically.
But this puzzle, it actually proves that yes,
it has its use.
It definitely has.
You just have to find the right problem matching it,
and yeah.
So yeah, once I understand it better,
the topic, I expect to write better code.
These new Emacs features, they work well enough.
There are some rough edges.
They definitely need more testing.
So please, please, everyone,
if you write Elisp, please try SQLite or Transient
or anything else that looks cool and shiny.
Report bugs.
Find ways to improve them, anything. And yeah,
I'm sure that if we do this,
then Emacs will continue to get even better.
So yeah, what's next for this package?
Well, I could... There are lots of obvious UI improvements
and testing to be done.
I basically want to reach feature parity
with the twisty-timer app, which this is very much inspired by.
I want nice-looking stats like graphical ones
instead of just a simple list of times.
And I want support for more puzzles, of course,
not just the simple cubes,
but as I progress learning these puzzles,
I want to have Emacs supporting me for this.
But generally, it's a very open-ended package.
And this concludes the talk.
Thank you very much.