About
States Machine is an app to memorize the names and locations of the fifty US states using spaced repetition.
It asks two kinds of questions:
- Given the name of a state, where is it located?
- Given a highlighted state, what is its name?
Screenshots
File format and spaced repetition
States Machine uses SQLite as its file format, and the SuperMemo 2 algorithm to plan repetitions.
The database contains 100 learning tasks, representing name and location for each state.
For each task, it stores
ef
: difficultyreps
: number of times this item has been seennext
: date on which this item should be tested again
(ef
and reps
are parameters from the SM2 algorithm)
We open a connection to the database at startup, and manipulate it as the program runs.
For example, picking the next item to test is as simple as
SELECT type, item, ef, reps FROM sm2
WHERE next IS NULL OR next <= strftime('%s', 'now')
ORDER BY RANDOM()
LIMIT 1
After each question is answered, the app asks us to rate its difficulty:
This is used to update its parameters and calculate when it should be tested next.
One cool side effect of using SQLite as the file format: you can also explore the database manually!
Remember, the ef
term represents how hard an item is to learn,
where lower values are scheduled more often.
We can use the database to check the hardest state:
sqlite> select type, item, ef from sm2 order by ef limit 10;
1|Wyoming|2.22
2|Pennsylvania|2.36
2|Alabama|2.36
1|Arkansas|2.36
2|Wyoming|2.36
2|Arizona|2.36
1|Iowa|2.5
2|Iowa|2.5
1|Delaware|2.5
2|Delaware|2.5
The type
column is 1
for "click on the state with the given name"
and 2
for "input the name of the highlighted state".
This means that the hardest state (for me)
is remembering the location of Wyoming.
GUI
The UI is entirely hand-rolled, and very application-specific.
It's drawn in three passes:
- Render map to offscreen framebuffer
- Render framebuffer to background (adding edge detection)
- Render UI on top (in a single draw call)
The map framebuffer renders each state in a different color:
This is used for both edge detection and to figure out which state is under the mouse (by reading back the framebuffer pixel).
Text is rendered using stb_truetype.h
,
from a copy of Inconsolata
that's bundled in the executable.
Shapes
The state shapes are based on genuine US Census Bureau shapefiles.
I implemented a slow, naive ear-clipping algorithm to triangulate them:
Speed doesn't matter here because the script is only run once, generating a C source file.
Size and speed
The full application is tiny, at about 2 MB unzipped and 1 MB zipped:
Everything is statically compiled into the executable, so it can be deployed without building an installer.
It is also fast, opening in under 600 milliseconds on a 2013 Macbook Pro:
Of course, interactions have no perceptible lag or loading time. Performance is a feature: if this is a program that folks use once per day, it has to be frictionless.
Source
The code lives on GitHub.