Implémentation de la frappe
Après toutes les préparations, nous pouvons enfin commencer à implémenter la fonction de frappe. Jetons un coup d'œil au code suivant :
function main() {
declare -i gamestarttime=0
declare -i gamedonetime=0
declare -i starttime
declare -i deadtime
declare -i curtime
declare -i donetime
declare -i numright=0
declare -i numtotal=0
declare -i accuracy=0
## Stocker les caractères correspondants dans un tableau, $1 représente le type de caractère sélectionné par l'utilisateur
putarray $1
## Initialiser le temps de début du jeu
gamestarttime=$(date +%s)
while [ 1 ]; do
echo -e "\033[2;2H Veuillez entrer la lettre à l'écran avant qu'elle ne disparaisse!"
echo -e "\033[3;2H Temps de jeu : "
curtime=$(date +%s)
gamedonetime=$curtime-$gamestarttime
echo -e "\033[31;40m\033[3;15H$gamedonetime s\033[37;40m"
echo -e "\033[3;60H Total : \033[31;26m$numtotal\033[37;40m"
echo -e "\033[3;30H Précision : \033[31;40m$accuracy %\033[37;40m"
echo -ne "\033[22;2H Votre saisie : "
clear_all_area
## Boucler 10 fois pour vérifier si une colonne de caractères a expiré ou a été touchée
for ((line = 20; line <= 60; line = line + 10)); do
## Vérifier si la colonne de caractères a été touchée
if [ "${ifchar[$line]}" == "" ] || [ "${donetime[$line]}" -gt "$time" ]; then
## Effacer l'affichage dans cette colonne
clear_line $line
## Générer un caractère aléatoire
if [ "$1" == "word" ]; then
read -u 4 word
if [ "$word" == "" ]; then ## Fin de la lecture du fichier
exec 4< $file
fi
putchar[$line]=$word
else
get_random_char $1
putchar[$line]=$random_char
fi
numtotal=$numtotal+1
## Définir le drapeau à 1
ifchar[$line]=1
## Réinitialiser le minuteur
starttime[$line]=$(date +%s)
curtime[$line]=${starttime[$line]}
donetime[$line]=$time
## Réinitialiser la position de la colonne à 0
column[$line]=0
if [ "$1" == "word" ]; then
move 0 $line ${putchar[$line]}
fi
else
## Si elle n'a pas expiré ou été touchée, mettre à jour le minuteur et la position actuelle
curtime[$line]=$(date +%s)
donetime[$line]=${curtime[$line]}-${starttime[$line]}
move ${donetime[$line]} $line ${putchar[$line]}
fi
done
if [ "$1"!= "word" ]; then
echo -ne "\033[22;14H" ## Effacer les caractères de la ligne de saisie
## Vérifier les caractères saisis par l'utilisateur et agir comme un minuteur d'une seconde
if read -n 1 -t 0.5 tmp; then
## Saisie réussie, boucle pour vérifier si la saisie correspond à une colonne
for ((line = 20; line <= 60; line = line + 10)); do
if [ "$tmp" == "${putchar[$line]}" ]; then
## Effacer l'affichage dans cette colonne
clear_line $line
## Si correspondance, effacer le drapeau
ifchar[$line]=""
echo -e "\007\033[32;40m\033[4;62H correct!\033[37;40m"
numright=$numright+1
## Sortir de la boucle
break
else
## Sinon, afficher toujours une erreur jusqu'à l'expiration
echo -e "\033[31;40m\033[4;62Hfaux, réessayez!\033[37;40m"
fi
done
fi
else
echo -ne "\033[22;14H" ## Effacer les caractères de la ligne de saisie
## Vérifier les caractères saisis par l'utilisateur et agir comme un minuteur
if read tmp; then
## Saisie réussie, boucle pour vérifier si la saisie correspond à une colonne
for ((line = 20; line <= 60; line = line + 10)); do
if [ "$tmp" == "${putchar[$line]}" ]; then
## Effacer l'affichage dans cette colonne
clear_line $line
## Si correspondance, effacer le drapeau
ifchar[$line]=""
echo -e "\007\033[32;40m\033[4;62H correct!\033[37;40m"
numright=$numright+1
## Sortir de la boucle
break
else
## Sinon, afficher toujours une erreur jusqu'à l'expiration
echo -e "\033[31;40m\033[4;62Hfaux, réessayez!\033[37;40m"
fi
done
fi
fi
trap " doexit " 2 ## Capturer des signaux spéciaux
## Calculer la précision
accuracy=$numright*100/$numtotal
done
}
Maintenant, expliquons séparément la méthode de frappe de caractères simples et de mots, car elles sont légèrement différentes.
Pour la frappe de caractères simples, nous voulons que cinq caractères apparaissent en même temps, et qu'ils continuent de tomber à un intervalle de temps défini jusqu'à ce qu'ils disparaissent lorsqu'ils atteignent une certaine hauteur (déterminée par la période d'expiration choisie au début). Si ils ne sont pas touchés (l'utilisateur n'entre pas le bon caractère correspondant) avant de disparaître, un nouveau caractère apparaîtra dans cette colonne. Par conséquent, tout d'abord, nous bouclons pour initialiser une séquence de caractères et la stockons dans un tableau. L'index du tableau représente le numéro de colonne du terminal, ce qui a l'avantage de gérer indépendamment chaque colonne de caractères, si elle a expiré ou a été touchée, et où elle devrait être placée. L'inconvénient est que nous allons créer un tableau relativement grand, mais il ne sera pas si grand qu'il ne puisse être géré. Cela ne pose pas de problème pour le shell car il n'alloue pas de mémoire en fonction de la taille de l'index du tableau. La première boucle for
est responsable de cela, et elle vérifie également à la fin de chaque grande boucle si une colonne est vide, puis un nouveau caractère apparaîtra dans cette colonne.
Ensuite, il y a une longue instruction if...else
. Vous vous souvenez du paramètre passé lors de l'appel de la fonction main
? La partie la plus extérieure est utilisée pour distinguer si l'on frappe des caractères simples ou des mots. Regardons d'abord la frappe de caractères simples, qui contient une ligne cruciale :
if read -n 1 -t 0.5 tmp
Le paramètre -n
de la commande read
spécifie la longueur des caractères à lire. Ici, une longueur de 1
est spécifiée, ce qui signifie que la saisie se termine immédiatement après que l'utilisateur a entré un caractère, sans avoir besoin d'appuyer sur la touche Entrée. Le paramètre -t
spécifie le temps d'expiration de la saisie. Si l'utilisateur n'entre rien ou si la saisie n'est pas terminée dans la période d'expiration, la lecture se terminera immédiatement. Parce que la commande read
est une opération synchrone pour le même shell lors de la lecture de la saisie de l'utilisateur, l'implémentation de la chute des caractères repose sur cet expiration (en implémentant indirectement un délai de chute). Ici, 0,5s
est défini car la plupart des utilisateurs peuvent terminer la saisie d'un caractère en moins de 0,5s
. Après avoir lu la saisie de l'utilisateur, il utilise une boucle for
pour comparer chaque tableau de caractères correspondant à chaque colonne, et s'il y a une correspondance, il appelle une fonction clear_line
pour effacer le caractère dans la colonne actuelle, et définit la variable de drapeau ifchar[$line]
sur 0
ce qui indique qu'il a été effacé.
Le flux de frappe de mots est essentiellement le même que celui de la frappe de caractères, sauf que parce que nous ne pouvons pas estimer le temps qu'il faut à l'utilisateur pour saisir un mot de longueur indéfinie, nous ne pouvons pas définir le temps d'expiration de la saisie pour la commande read
. Sans cet expiration, la mise en œuvre finale de la frappe de mots peut ne pas tomber automatiquement comme lors de la frappe de caractères, mais plutôt que le mot tombe d'une ligne après que nous ayons saisi un mot et appuyé sur Entrée. Bien sûr, vous pouvez envisager d'utiliser d'autres méthodes pour obtenir les mêmes ou de meilleurs résultats que la frappe de caractères.
De plus, la gestion de l'obtention de nouveaux mots pour chaque colonne est légèrement différente car nous lisons à partir d'un fichier, plutôt que de générer des mots aléatoires. Vous vous souvenez toujours du descripteur de fichier 4
que nous avons créé plus tôt, qui pointe vers un fichier de mots? Nous avons utilisé ce descripteur de fichier ici :
read -u 4 word
if [ "$word" == "" ]; then ## Fin de la lecture du fichier
exec 4< $file
fi
putchar[$line]=$word
Ici, nous utilisons toujours la commande read
, et avec le paramètre -u
, nous pouvons spécifier à partir de quel descripteur de fichier lire le fichier ligne par ligne dans la variable. La déclaration de vérification de vide suivante est pour recréer le descripteur de fichier qui pointe vers le fichier lorsqu'un fichier atteint sa fin, afin que read
puisse lire à partir du début du fichier à nouveau.
Vous trouvez cela un peu compliqué? Ne vous inquiétez pas, nous n'avons pas fini. Ensuite, faites attention à l'avant-dernière ligne de la fonction main
:
trap " doexit " 2 ## Capturer des signaux spéciaux
La commande trap
est utilisée pour capturer des signaux spéciaux dans le shell, tels que Ctrl+C
, Ctrl+D
, Ctrl+Z
, ESC
, etc. À l'origine, ces signaux spéciaux sont gérés par le shell lui-même. Parce que nous voulons que le jeu quitte plus gracieusement lorsqu'il se termine, nous pouvons intercepter le 2ème signal spécial, qui est Ctrl+C
, pour implémenter certains traitements personnalisés. Par exemple, ici, après avoir capturé le 2ème signal, il appelle la fonction doexit
ci-dessous pour quitter le programme gracieusement :
function doexit() {
draw_border
echo -e "\033[10;30Hce jeu va se terminer....."
echo -e "\033[0m"
sleep 2
clear
exit 1
}