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?
File format and spaced repetition
The database contains 100 learning tasks, representing name and location for each state.
For each task, it stores
reps: number of times this item has been seen
next: date on which this item should be tested again
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!
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
type column is
1 for "click on the state with the given name"
2 for "input the name of the highlighted state".
This means that the hardest state (for me)
is remembering the location of Wyoming.
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).
The state shapes are based on genuine US Census Bureau shapefiles.
I implemented a slow, naive ear-clipping algorithm to triangulate them:
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.
The code lives on GitHub.