DEV Community 👩‍💻👨‍💻

Cover image for Flashcards in the terminal
Daniel Fitzpatrick
Daniel Fitzpatrick

Posted on

Flashcards in the terminal

Several weeks ago, I wrote a webdriver app in Clojure to pull directory information from congregate websites. The general idea was this:

I have trouble remembering names and faces. What if I had a flashcard system to help me memorize them?

However, I got bored before I could finish.

Rivendell update

I've been hard at work this week updating Rivendell for Elvish v0.17. I recently finished testing and documenting the fun module, which houses most of the functional pieces. You can view the docs here.

I have another blog post about how I generated that documentation if you find that interesting.

With the progress made on Rivendell, I think now would be an excellent time to finish the flashcard app.

Setup instructions

Since Rivendell is currently under development, I am using Elvish's rsync functionality.

Clone rivendell and switch to the update-for-0.17 branch. Then place this file in ~/.local/share/elvish/lib/dev/epm-domain.cfg

{
    "method": "rsync",
    "location": <path to rivendell parent directory>,
    "levels": "1"
}
Enter fullscreen mode Exit fullscreen mode

Then, add this to your ~/.config/elvish/rc.elv.

use epm
epm:install &silent-if-installed=$true dev/rivendell
epm:upgrade dev/rivendell
use dev/rivendell/fun
Enter fullscreen mode Exit fullscreen mode

Last, you'll need a terminal that understands the Terminal Graphics Protocol. I'll be using Kitty.

Needed functions

The functions we need from Rivendell are shuffle and destruct. Next, we need a way to display images. If you want to replicate my environment, I have this function defined in my rc.elv for displaying images.

fn icat {|@a| kitty +kitten icat $@a }
Enter fullscreen mode Exit fullscreen mode

Next, we need a way to score guesses. For that, let's define a simple Levenshtein function. Low scores are better.

use dev/rivendell/base b
use dev/rivendell/fun f
use math

fn levenshtein {|w1 w2|
  var cell-value = {|same-char prev-row cur-row col-idx|
    math:min (b:inc $prev-row[$col-idx]) ^
             (b:inc (b:end $cur-row)) ^
             (+ (if $same-char { put 0 } else { put 1 }) ^
                $prev-row[(b:dec $col-idx)])
  }

  var row-idx = 1
  var max-rows = (b:inc (count $w2))
  var @prev-row = (range (b:inc (count $w1)))

  while (!= $row-idx $max-rows) {
    var ch2 = $w2[(b:dec $row-idx)]
    set row-idx = (b:inc $row-idx)
    set prev-row = (f:reduce {|cur-row i|
        var same-char = (eq $w1[(b:dec $i)] $ch2)
        b:append $cur-row ($cell-value $same-char $prev-row $cur-row $i)
    } [$row-idx] (range 1 (count $prev-row)))
  }

  b:end $prev-row
}
Enter fullscreen mode Exit fullscreen mode

That code takes advantage of Elvish's module aliasing feature and also demonstrates a few more simple Rivendell functions, reduce being the most interesting.

I placed this code in a file called levenshtein.elv and imported it with use ./levenshtein l.

Next, we need a function to tie these together. It's not fancy. I just defined this in the terminal.

fn guess {|name image|
  icat $image; echo
  var guess = (read-line)
  var score = (l:levenshtein $name $guess)
  put $name $score
}
Enter fullscreen mode Exit fullscreen mode

We have all the components now. So let's pull our directory info and start scoring guesses.

Running the flashcard "app"

Is this thing categorically different from an app? Probably.

After you've built the congr project from the first post, execute with:

var @directory = (java -jar target/uberjar/congr-0.1.0-SNAPSHOT-standalone.jar site username password)
Enter fullscreen mode Exit fullscreen mode

That last command may take several seconds.

Next, shuffle it.

var @shuffled = (each {|x| echo $x | from-json} $directory | fun:shuffle)
Enter fullscreen mode Exit fullscreen mode

Last, start guessing.

each (fun:destruct $guess~) $shuffled
Enter fullscreen mode Exit fullscreen mode

For privacy reasons I won't show actual names and photos from the directory, but you can view a screenshot with mocked information from knowyourmeme.com.

action shot

Conclusion

I always envisioned a stupid simple terminal "app" for this project, so it appears I succeeded. Furthermore, I appreciate that Clojure and Elvish worked together to make the experience genuinely hacker-ish.

Happy hacking!

Top comments (0)

12 Rarely Used Javascript APIs You Need

Practical examples of some unique Javascript APIs that beautifully demonstrate a practical use-case.