Implementierung des Tippens
Nach all den Vorbereitungen können wir endlich mit der Implementierung der Tippfunktion beginnen. Schauen wir uns den folgenden Code an:
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
## Speichert die entsprechenden Zeichen in einem Array. $1 repräsentiert den vom Benutzer ausgewählten Zeichensatz.
putarray $1
## Initialisiert die Spielstartzeit
gamestarttime=$(date +%s)
while [ 1 ]; do
echo -e "\033[2;2H Bitte geben Sie den Buchstaben auf dem Bildschirm ein, bevor er verschwindet!"
echo -e "\033[3;2H Spielzeit: "
curtime=$(date +%s)
gamedonetime=$curtime-$gamestarttime
echo -e "\033[31;40m\033[3;15H$gamedonetime s\033[37;40m"
echo -e "\033[3;60H Gesamt: \033[31;26m$numtotal\033[37;40m"
echo -e "\033[3;30H Genauigkeit: \033[31;40m$accuracy %\033[37;40m"
echo -ne "\033[22;2H Ihre Eingabe: "
clear_all_area
## Schleife 10 Mal, um zu prüfen, ob eine Spalte von Zeichen abgelaufen oder getroffen wurde.
for ((line = 20; line <= 60; line = line + 10)); do
## Prüft, ob die Spalte von Zeichen getroffen wurde.
if [ "${ifchar[$line]}" == "" ] || [ "${donetime[$line]}" -gt "$time" ]; then
## Löscht die Anzeige in dieser Spalte.
clear_line $line
## Generiert ein zufälliges Zeichen.
if [ "$1" == "word" ]; then
read -u 4 word
if [ "$word" == "" ]; then ## Ende des Dateilesens
exec 4< $file
fi
putchar[$line]=$word
else
get_random_char $1
putchar[$line]=$random_char
fi
numtotal=$numtotal+1
## Setzt das Flag auf 1
ifchar[$line]=1
## Setzt den Timer zurück
starttime[$line]=$(date +%s)
curtime[$line]=${starttime[$line]}
donetime[$line]=$time
## Setzt die Spaltenposition auf 0 zurück
column[$line]=0
if [ "$1" == "word" ]; then
move 0 $line ${putchar[$line]}
fi
else
## Wenn es nicht abgelaufen oder getroffen wurde, aktualisiert den Timer und die aktuelle Position.
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" ## Löscht die Eingabezeilenzeichen
## Prüft die Benutzereingabezeichen und fungiert als Ein-Sekunden-Timer
if read -n 1 -t 0.5 tmp; then
## Erfolgreiche Eingabe, Schleife prüft, ob die Eingabe einer Spalte entspricht.
for ((line = 20; line <= 60; line = line + 10)); do
if [ "$tmp" == "${putchar[$line]}" ]; then
## Löscht die Anzeige in dieser Spalte.
clear_line $line
## Wenn übereinstimmend, löscht das Flag.
ifchar[$line]=""
echo -e "\007\033[32;40m\033[4;62H richtig!\033[37;40m"
numright=$numright+1
## Beendet die Schleife
break
else
## Andernfalls zeigt immer einen Fehler an, bis der Zeitablauf erreicht ist.
echo -e "\033[31;40m\033[4;62Hfalsch, versuchen Sie es erneut!\033[37;40m"
fi
done
fi
else
echo -ne "\033[22;14H" ## Löscht die Eingabezeilenzeichen
## Prüft die Benutzereingabezeichen und fungiert als Timer
if read tmp; then
## Erfolgreiche Eingabe, Schleife prüft, ob die Eingabe einer Spalte entspricht.
for ((line = 20; line <= 60; line = line + 10)); do
if [ "$tmp" == "${putchar[$line]}" ]; then
## Löscht die Anzeige in dieser Spalte.
clear_line $line
## Wenn übereinstimmend, löscht das Flag.
ifchar[$line]=""
echo -e "\007\033[32;40m\033[4;62H richtig!\033[37;40m"
numright=$numright+1
## Beendet die Schleife
break
else
## Andernfalls zeigt immer einen Fehler an, bis der Zeitablauf erreicht ist.
echo -e "\033[31;40m\033[4;62Hfalsch, versuchen Sie es erneut!\033[37;40m"
fi
done
fi
fi
trap " doexit " 2 ## Fängt spezielle Signale ab
## Berechnet die Genauigkeit
accuracy=$numright*100/$numtotal
done
}
Nun erklären wir die Methode des Tippens von einzelnen Zeichen und Wörtern separat, da sie sich etwas unterscheiden.
Beim Tippen von einzelnen Zeichen möchten wir, dass fünf Zeichen gleichzeitig erscheinen und in einem festgelegten Zeitintervall weiterfallen, bis sie verschwinden, wenn sie eine bestimmte Höhe erreichen (bestimmt durch die am Anfang gewählte Zeitüberschreitung). Wenn sie vor dem Verschwinden nicht getroffen werden (der Benutzer gibt nicht das entsprechende richtige Zeichen ein), erscheint in dieser Spalte ein neues Zeichen. Daher initialisieren wir zunächst in einer Schleife eine Zeichenfolge und speichern sie in einem Array. Der Index des Arrays repräsentiert die Spaltennummer des Terminals. Dies hat den Vorteil, dass wir jede Spalte von Zeichen unabhängig verwalten können, ob sie abgelaufen oder getroffen wurde und wo sie platziert werden soll. Der Nachteil ist, dass wir ein relativ großes Array erstellen werden, aber es wird nicht so groß sein, dass es nicht gehandhabt werden kann. Dies spielt für die Shell keine Rolle, da sie nicht basierend auf der Größe des Arrayindexes Speicher zuweist. Die erste for
-Schleife ist dafür verantwortlich, und sie prüft auch am Ende jeder Hauptschleife, ob eine Spalte leer ist, und dann erscheint in dieser Spalte ein neues Zeichen.
Als nächstes folgt eine lange if...else
-Anweisung. Erinnern Sie sich noch an den Parameter, der beim Aufruf der main
-Funktion übergeben wurde? Der äußerste Teil wird verwendet, um zu unterscheiden, ob einzelne Zeichen oder Wörter getippt werden sollen. Schauen wir uns zunächst das Tippen von einzelnen Zeichen an, das eine entscheidende Zeile enthält:
if read -n 1 -t 0.5 tmp
Der -n
-Parameter des read
-Befehls gibt die Länge der zu lesenden Zeichen an. Hier wird eine Länge von 1
angegeben, was bedeutet, dass die Eingabe sofort endet, nachdem der Benutzer ein Zeichen eingegeben hat, ohne dass die Eingabetaste gedrückt werden muss. Der -t
-Parameter gibt die Eingabezeitüberschreitung an. Wenn der Benutzer innerhalb der Zeitüberschreitung keine Eingabe macht oder die Eingabe nicht abgeschlossen ist, endet das Lesen sofort. Da der read
-Befehl beim Lesen der Benutzereingabe für die gleiche Shell eine synchrone Operation ist, basiert die Implementierung des Fallens von Zeichen auf dieser Zeitüberschreitung (indirekt wird eine Verzögerung beim Fallen implementiert). Hier wird 0,5s
festgelegt, da die meisten Benutzer die Eingabe eines Zeichens innerhalb von 0,5s
abschließen können. Nachdem die Benutzereingabe gelesen wurde, wird eine for
-Schleife verwendet, um jedes Zeichenarray, das jeder Spalte entspricht, zu vergleichen. Wenn es eine Übereinstimmung gibt, wird die clear_line
-Funktion aufgerufen, um das Zeichen in der aktuellen Spalte zu löschen, und die Flag-Variable ifchar[$line]
wird auf 0
gesetzt, was bedeutet, dass es gelöscht wurde.
Der Ablauf des Tippens von Wörtern ist im Grunde der gleiche wie beim Tippen von Zeichen, nur dass wir, da wir die Zeit nicht abschätzen können, die der Benutzer benötigt, um ein Wort mit unbestimmter Länge einzugeben, keine Eingabezeitüberschreitung für den read
-Befehl festlegen können. Ohne diese Zeitüberschreitung kann die endgültige Implementierung des Tippens von Wörtern möglicherweise nicht automatisch fallen wie beim Tippen von Zeichen, sondern das Wort fällt eine Zeile nach unten, nachdem wir ein Wort eingegeben und die Eingabetaste gedrückt haben. Natürlich können Sie andere Methoden in Betracht ziehen, um die gleichen oder bessere Effekte wie beim Tippen von Zeichen zu erzielen.
Darüber hinaus ist die Behandlung des Abrufs neuer Wörter für jede Spalte etwas anders, da wir aus einer Datei lesen, anstatt zufällige Wörter zu generieren. Erinnern Sie sich noch an den Dateideskriptor 4
, den wir früher erstellt haben, der auf eine Wortdatei zeigt? Wir haben diesen Dateideskriptor hier verwendet:
read -u 4 word
if [ "$word" == "" ]; then ## Ende des Dateilesens
exec 4< $file
fi
putchar[$line]=$word
Hier verwenden wir weiterhin den read
-Befehl, und mit dem -u
-Parameter können wir angeben, aus welchem Dateideskriptor die Datei zeilenweise in die Variable gelesen werden soll. Die nachfolgende Leerprüfungsanweisung dient dazu, den Dateideskriptor, der auf die Datei zeigt, neu zu erstellen, wenn eine Datei zu Ende ist, damit read
wieder von Anfang der Datei lesen kann.
Fühlen Sie sich ein bisschen überfordert? Keine Sorge, wir sind noch nicht fertig. Achten Sie nun auf die vorletzte Zeile der main
-Funktion:
trap " doexit " 2 ## Fängt spezielle Signale ab
Der trap
-Befehl wird verwendet, um spezielle Signale in der Shell abzufangen, wie z. B. Ctrl+C
, Ctrl+D
, Ctrl+Z
, ESC
usw. Ursprünglich werden diese speziellen Signale von der Shell selbst behandelt. Da wir möchten, dass das Spiel beim Beenden eleganter beendet wird, können wir das 2. spezielle Signal, das Ctrl+C
ist, abfangen, um einige benutzerdefinierte Verarbeitungen zu implementieren. Beispielsweise ruft es hier nach dem Abfangen des 2. Signals die folgende doexit
-Funktion auf, um das Programm elegant zu beenden:
function doexit() {
draw_border
echo -e "\033[10;30HDieses Spiel wird beendet....."
echo -e "\033[0m"
sleep 2
clear
exit 1
}