Last active
August 19, 2024 09:21
-
-
Save grondilu/7259903ad288af0a49027f5adadba8a4 to your computer and use it in GitHub Desktop.
A mock-up for a terminal-based spaced repetition learning system
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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