Skip to content

Instantly share code, notes, and snippets.

@grondilu
Last active August 19, 2024 09:21
Show Gist options
  • Save grondilu/7259903ad288af0a49027f5adadba8a4 to your computer and use it in GitHub Desktop.
Save grondilu/7259903ad288af0a49027f5adadba8a4 to your computer and use it in GitHub Desktop.
A mock-up for a terminal-based spaced repetition learning system
#!/usr/bin/env bash
# Inspired from [flash.sh](https://github.com/tallguyjenks/fla.sh)
# See original license further down
#
# Copyright © 2024, Lucien Grondin (grondilu on github)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the “Software”), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS X BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# Original license:
#
## MIT License
##
## Copyright (c) 2020 Bryan Jenks
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included in all
## copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
set -u
declare -r ALPHA=10
declare -ri TIME_SCALE=3600*24*365
declare -ra DECK=(
"History:When was the declaration of independence signed?:1776:0"
"Math:What is the square root of 4?:2:4"
"Science:What is the charge on a proton?:Positive 1:2"
"Philosophy:What was Socrates known as?:The Gadfly of Athens:0"
"Programming:What is the typical starting index of an array?:0:5"
"History:What did Abraham Lincoln typically keep in his hat?:Mail:0"
"Math:What is the value of PI to 2 decimal places?:3.14:4"
"Science:What is the charge on an electron?:Negative 1:3"
"Programming:What does OOP stand for?:Object Oriented Programming:2"
"History:What did Socrates drink to commit suicide?:Hemlock Tea:2"
"Math:What is the general equation for the slope of a line?:y=mx+b:4"
"Science:What is the charge on a neutron?:Neutral:1"
"Programming:What is Vim?:God's Text Editor:999"
"History:What were the British known by during the American Revolution?:The Redcoats:1"
"Math:What is the value of this equation - log10(100)?:2:0"
"Science:What are protons and neutrons made of?:quarks:5"
"Programming:What does RAM stand for?:Random Access Memory:1"
"History:What was the year 2000 also known as?:Y2K:0"
"Math:What is the formula for the mean?:Sum/count:4"
"Science:What is cold?:The absence of heat:3"
"Programming:What languages are the worst?:Proprietary:999"
"History:When did man land on the moon?:1969:4"
"Math:10^3=?:1000:1"
"Science:The _____ ______ Project mapped all of man's genes.:Human Genome:3"
"Programming:What is the best computer to program on?:Thinkpad:999"
"History:When was fla.sh created?:April 2020:999"
"Math:What do you call a number only divisible by itself and 1?:Prime:0"
"Science:What is the distance between the Earth and Sol called?:An Astronomical Unit (AU):1"
"Programming:What is the best operating system?:Arch, because BTW i run Arch:999"
)
declare -a LOG=()
center() {
local line="$1"
echo "$line" |
sed $'s/\e\[[0-9;]*m//g' |
{
read -r
echo -e "\e[$(( (${COLUMNS:-$(tput cols)}-${#REPLY})/2 + 1))G$line"
}
}
softmax() {
local alpha="${1:-1.0}"
local -i limit=50
awk -v alpha="$alpha" -v limit=$limit -e '{
x = alpha*$1;
sum += e[$2] = exp(x<-limit ? -limit : (x > limit ? limit : x))
}
END { for (i in e) { print e[i]/sum, i } }'
}
if ((${#LOG[@]} == 0))
then
# Log is empty, so to get started
# we'll fill it with failed attempts an hour ago.
declare -i i timestamp=$(date +%s)-3600
for ((i=0;i<${#DECK[@]};i++))
do LOG+=("$timestamp:$timestamp:$i:0")
done
fi
# hiding the cursor
echo -ne "\e[?25l"
declare line
while
for line in "${LOG[@]}"
do echo "$line"
done |
awk -F: -v now=$(date +%s) -v tScale=$TIME_SCALE -e '{
deltaT = now - $1;
if ($4) { s[$3]=-deltaT/tScale } else {
if (s[$3] < 0) { s[$3]=tScale/deltaT } else { s[$3]+=tScale/deltaT }
}
}
END { for (i in s) print +s[i], i }' |
softmax "-$ALPHA" |
LANG=C sort -t' ' -g -k1 |
awk -v x="$((RANDOM%32768))" -e 'BEGIN { x /= 32768 }
{ if ( (s+=$1) > x ) { print $2; found=1; exit } }
END { if (found==0) { print $2 } }
' |
{
declare -i line_choice
read -r line_choice
declare -a record
IFS=: read -ra record <<<"${DECK[line_choice]}"
declare -i t1=$(date +%s) width
((
width=${COLUMNS:-$(tput cols)},
width=width < 80 ? width : 80
))
clear
echo -n "${record[1]}" |
fmt -w $width |
{
mapfile -t
echo -ne "\e[H"
echo -ne "\e[$(( (${LINES:-$(tput lines)}-${#MAPFILE[@]})/2))B"
for line in "${MAPFILE[@]}"
do center "$line"
done
read -sn1 </dev/tty
echo -ne "\e[1A\e[2J"
declare -i t2=$(date +%s)
case "$REPLY" in
$' ')
center $'\e[31m'"${record[2]}"$'\e[0m'
LOG+=("$t1:$t2:$line_choice:1")
sleep 1
;;
$'')
center $'\e[32m'"${record[2]}"$'\e[0m'
LOG+=("$t1:$t2:$line_choice:0")
sleep .2
;;
*)
# show the cursor
echo -e "\e[?25h"
exit 1
esac
}
}
do sleep .01
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment