Einführung
In diesem Projekt lernen Sie, wie Sie mithilfe eines Shell-Skripts ein einfaches Tippspiel erstellen können. Das Spiel zeigt zufällige Zeichen auf dem Bildschirm an, und Ihr Ziel ist es, sie einzugeben, bevor sie verschwinden. Das Spiel bietet verschiedene Modi mit unterschiedlichen Schwierigkeitsgraden. Sie können wählen, ob Sie die Eingabe von Zahlen, Buchstaben, einer Mischung aus beiden oder sogar Ihrer eigenen benutzerdefinierten Wörter üben möchten.
👀 Vorschau

🎯 Aufgaben
In diesem Projekt lernen Sie:
- Wie Sie eine Projekt-Datei erstellen und sie in einem Code-Editor öffnen
- Wie Sie mithilfe von Sonderzeichen und Farben eine Willkommensoberfläche anzeigen
- Wie Sie ein Modiauswahlmenü implementieren, um den Schwierigkeitsgrad auszuwählen
- Wie Sie ein Tippkategorie-Auswahlmenü implementieren, um die Art der Zeichen auszuwählen, die Sie üben möchten
- Wie Sie Funktionen zum Zeichnen einer Umrandung und Füllen der Hintergrundfarbe der Tippoberfläche erstellen
- Wie Sie zufällige Buchstaben und Zahlen für das Tippspiel generieren
- Wie Sie die Tippfunktionalität implementieren, einschließlich der Verarbeitung von Benutzereingaben und der Berechnung der Genauigkeit
- Wie Sie eine gracefull Exit-Funktion erstellen, um spezielle Signale zu verarbeiten
🏆 Errungenschaften
Nach Abschluss dieses Projekts können Sie:
- Die Grundlagen des Shell-Scriptings demonstrieren
- Sonderzeichen und Farben in der Terminalausgabe verwenden
- Eingaben vom Benutzer in Shell-Skripten lesen
- Menüs und Benutzeroberflächen in Shell-Skripten implementieren
- Spezielle Signale in Shell-Skripten verarbeiten
Projektdatei erstellen
Um zu beginnen, erstellen Sie bitte eine neue Datei mit dem Namen shell_typing_game.sh und öffnen Sie sie in Ihrem bevorzugten Code-Editor.
cd ~/project
touch shell_typing_game.sh
Willkommensbildschirm anzeigen
#!/bin/bash
function dis_welcome() {
declare -r str='
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
1000000010000000101111101000000111100111000000100000001000001111
0100000101000001001000001000001000001000100001010000010100001000
0010001000100010001111101000001000001000100010001000100010001111
0001010000010100001000001000001000001000100100000101000001001000
0000100000001000001111101111100111100111001000000010000000101111
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000001111000000100000000001000000010000001111100000000000
0000000000010000000001010000000010100000101000001000000000000000
0000000000010011000011111000000100010001000100001111100000000000
0000000000010001000100000100001000001010000010001000000000000000
0000000000001111001000000010010000000100000001001111100000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000'
declare -i j=0
declare -i row=3 ## Print start line.
line_char_count=65 ## Define the newline position, 64 characters per line plus newlines, for a total of 65 characters.
echo -ne "\033[37;40m\033[5;3H" ## Set the color and cursor start position.
for ((i = 0; i < ${#str}; i++)); do
## Line feed.
if [ "$((i % line_char_count))" == "0" ] && [ "$i"!= "0" ]; then
row=$row+1
echo -ne "\033["$row";3H"
fi
## Determine foreground and background characters.
if [ "${str:$i:1}" == "0" ]; then
echo -ne "\033[37;40m "
elif [ "${str:$i:1}" == "1" ]; then
echo -ne "\033[31;40m$"
fi
done
}
dis_welcome
Die lange Zeichenkette am Anfang 010101... ist die Willkommensoberfläche des Spiels, die angezeigt werden soll. Wenn Sie sie direkt ansehen, verstehen Sie vielleicht nicht, was es ist. Sie können diese Zeichenkette also in einen gedit-Editor kopieren und dann die Tastenkombination Strg+F drücken, um zu suchen. Wenn Sie 1 eingeben, verstehen Sie es sofort. Konzentrieren wir uns jetzt auf den echo-Befehl. Wir müssen den echo-Befehl verwenden, weil wir die Oberfläche auf dem Bildschirm anzeigen möchten (hier bezieht sich dies auf den Xfce-Terminal. Die Standard-Eingabe, -Ausgabe und Standard-Fehlerausgabe unter Linux sind standardmäßig mit dem Terminal verbunden). Um einige spezielle Funktionen zu erreichen, wie z. B. das Drucken an einer bestimmten Position, das Einstellen der Anzeigefarbe der Zeichen und das Drucken des Hintergrunds des gesamten Spiels, müssen wir einige spezielle Parameter des entsprechenden Befehls verwenden, wie im folgenden Code gezeigt:
## Set the color and cursor start position.
echo -ne "\033[37;40m\033[5;3H"
Basierend auf den Kommentaren können wir ungefähr verstehen, was diese Zeile tut. Aber was genau bedeutet sie? Zunächst bedeutet der Parameter -n, dass in der aktuellen Zeile gedruckt wird, ohne nach der Ausgabe eine neue Zeile zu beginnen. Dann wird die Ausgabezeichenfarbe und die Cursorposition festgelegt. Dabei ist \033[Vordergrundfarbe;Hintergrundfarbe m, und danach folgt \033[Zeile;Spalte H. Um jedoch zu ermöglichen, dass echo diese speziellen Escape-Sequenzen erkennt, benötigen wir den Parameter -e, andernfalls wird echo diese Zeichen einfach so ausgeben.
Vordergrundfarben: 30 - 39, Hintergrundfarben: 40 - 49.
| Wert | Vordergrundfarben | Wert | Hintergrundfarben (Bereich: 40 - 49) |
|---|---|---|---|
| 30 | Setzt schwarzen Vordergrund | 40 | Setzt schwarzen Hintergrund |
| 31 | Setzt roten Vordergrund | 41 | Setzt roten Hintergrund |
| 32 | Setzt grünen Vordergrund | 42 | Setzt grünen Hintergrund |
| 33 | Setzt braunen Vordergrund | 43 | Setzt braunen Hintergrund |
| 34 | Setzt blauen Vordergrund | 44 | Setzt blauen Hintergrund |
| 35 | Setzt lila Vordergrund | 45 | Setzt lila Hintergrund |
| 36 | Setzt cyan Vordergrund | 46 | Setzt cyan Hintergrund |
| 37 | Setzt weißen Vordergrund | 47 | Setzt weißen Hintergrund |
| 38 | Setzt Unterstreichung auf Standard-Vordergrundfarbe | 49 | Setzt Standard-schwarzen Hintergrund |
| 39 | Entfernt Unterstreichung von Standard-Vordergrundfarbe |
Hätten Sie nicht gedacht, dass echo so verwendet werden kann! Fühlen Sie sich wie neu mit ihm vertraut gemacht? Okay, lassen Sie uns jetzt diesen Codeausschnitt im Terminal testen.
cd ~/project
bash shell_typing_game.sh

Modusauswahlmenü anzeigen
## Deklariere die Variable 'time', um das Tippzeitlimit darzustellen, wobei verschiedene Schwierigkeitsgrade unterschiedlichen Zeitlimits entsprechen
declare -i time
function modechoose() {
## Wähle aus drei verschiedenen Modi
echo -e "\033[8;30H1) Leichter Modus"
echo -e "\033[9;30H2) Normaler Modus"
echo -e "\033[10;30H3) Schwieriger Modus"
echo -ne "\033[22;2HBitte geben Sie Ihre Wahl ein: "
read mode
case $mode in
"1")
time=10
dismenu ## Rufe die Menüauswahlfunktion auf
;;
"2")
time=5
dismenu
;;
"3")
time=3
dismenu
;;
*)
echo -e "\033[22;2HIhre Wahl ist inkorrekt. Bitte versuchen Sie es erneut."
sleep 1
;;
esac
}
Bevor wir diese Funktion definieren, haben wir zunächst eine Ganzzahlvariable 'time' mit dem Befehl declare -i deklariert. Dann haben wir in der folgenden case-Anweisung basierend auf der Schwierigkeitsauswahl des Benutzers verschiedene Werte für die Variable 'time' festgelegt. Je höher die Schwierigkeit, desto kleiner der Wert. Diese Variable wird tatsächlich in der nachfolgenden Tippverarbeitungsfunktion verwendet. Sie erinnern sich vielleicht, dass in Shell-Skripten Variablen, die innerhalb oder außerhalb einer Funktion deklariert oder definiert werden, als globale Variablen behandelt werden, deren Gültigkeitsbereich sich über die gesamte Skriptdatei erstreckt, es sei denn, das Schlüsselwort 'local' wird verwendet, um die Variable innerhalb einer Funktion zu deklarieren. Was den Anzeigeeffekt des Menüs betrifft, verwenden wir weiterhin den echo-Befehl und dann den read-Befehl, um die Eingabe des Benutzers in die Variable 'mode' zu lesen.

Tippkategorie-Auswahlmenü anzeigen
function display_menu() {
while [ 1 ]; do
draw_border
echo -e "\033[8;30H1) Üben Sie das Tippen von Zahlen"
echo -e "\033[9;30H2) Üben Sie das Tippen von Buchstaben"
echo -e "\033[10;30H3) Üben Sie das Tippen von alphanumerischen Zeichen"
echo -e "\033[11;30H4) Üben Sie das Tippen von Wörtern"
echo -e "\033[12;30H5) Beenden"
echo -ne "\033[22;2HBitte geben Sie Ihre Wahl ein: "
read choice
case $choice in
"1")
draw_border
## Die nächsten beiden sind Funktionsparameter. Der erste Parameter gibt den Tipptyp an, und der zweite ist die Funktion zum Bewegen von Zeichen.
main digit
echo -e "\033[39;49m"
;;
"2")
draw_border
main char
echo -e "\033[39;49m"
;;
"3")
draw_border
main mix
echo -e "\033[39;49m"
;;
"4")
draw_border
echo -ne "\033[22;2H"
read -p "Welche Datei möchten Sie für das Tippen üben: " file
if [! -f "$file" ]; then
display_menu
else
exec 4< $file ## Erstellen Sie eine Dateipipeline
main word
echo -e "\033[39;49m"
fi
;;
"5" | "q" | "Q")
draw_border
echo -e "\033[10;25HSie verlassen jetzt das Spiel."
echo -e "\033[39;49m"
sleep 1
clear
exit 1
;;
*)
draw_border
echo -e "\033[22;2HIhre Wahl ist falsch. Bitte versuchen Sie es erneut."
sleep 1
;;
esac
done
}
In dieser Funktion erklären wir zunächst die beiden Funktionen, die in der case-Verzweigung aufgerufen werden. Zunächst wird die draw_border-Funktion verwendet, um die Umrandung der Tippoberfläche zu zeichnen, was später gezeigt wird. Dann wird die main-Funktion aufgerufen. Diese main-Funktion hat keine besondere Rolle wie die main-Funktion in Sprachen wie Java und C. Sie hat einfach den Namen main, um anzuzeigen, dass sie in ganzem Programm eine Hauptrolle spielt. Sie haben vielleicht bemerkt, dass es nach jeder main ein Argument gibt. Ja, dies ist der an die Funktion übergebene Parameter. In der Shell werden Parameter nicht wie in vielen anderen Sprachen in Klammern direkt hinter dem Funktionsnamen geschrieben. Es sollte auch beachtet werden, dass in der vierten Verzweigung der case-Anweisung, also in diesen Zeilen:
read -p "Which file would you like to use for typing practice: " file
if [! -f "$file" ]; then
display_menu
else
exec 4< $file ## Create a file pipeline
main word
echo -e "\033[39;49m"
fi
Gemäß der Menüaufforderung können wir erkennen, dass diese Verzweigung für das Üben des Tippens von Wörtern vorgesehen ist. Dieses Programm ermöglicht es Benutzern, benutzerdefinierte Wortdateien (Textdateien mit bestimmten Formatierungsanforderungen, wobei jedes Wort in einer eigenen Zeile steht. Sie können eine Wortlisten-Datei online herunterladen und den awk-Befehl verwenden, um sie zu extrahieren) für die Übung zu nutzen. Daher müssen wir zunächst einen vom Benutzer eingegebenen Dateinamen einlesen, um die Wortdatei auszuwählen. Dann verwenden wir den exec-Befehl, um eine Pipeline zu erstellen, die auf die Datei zeigt, d. h., wir leiten die Ausgabe der Datei an den Dateideskriptor 4 (fd) um. Da exec ein eingebauter Befehl in bash ist, können Sie die Dokumentation von exec nicht mit dem man-Befehl anzeigen. Die I/O-Umleitung mit exec hängt normalerweise mit fd zusammen, und die Shell hat normalerweise 10 fd, die von 0 bis 9 reichen. Die häufig verwendeten fd sind 3, nämlich 0 (stdin, Standard-Eingabe), 1 (stdout, Standard-Ausgabe) und 2 (stderr, Standard-Fehlerausgabe). Versuchen Sie es einfach zu verstehen.

Zeichnen einer Umrandung für eine Tippoberfläche
function draw_border() {
declare -i width
declare -i high
width=79 ## Standardbreite des Terminals - 1
high=23 ## Standardhöhe des Terminals - 1
clear
## Setze die Anzeigefarbe auf weiß auf schwarzem Hintergrund
echo -e "\033[37;40m"
## Setze die Hintergrundfarbe
for ((i = 1; i <= $width; i = i + 1)); do
for ((j = 1; j <= $high; j = j + 1)); do
## Setze die Anzeigeposition
echo -e "\033["$j";"$i"H "
done
done
## Zeichne die Hintergrundumrandung
echo -e "\033[1;1H+\033["$high";1H+\033[1;"$width"H+\033["$high";"$width"H+"
for ((i = 2; i <= $width - 1; i = i + 1)); do
echo -e "\033[1;"$i"H-"
echo -e "\033["$high";"$i"H-"
done
for ((i = 2; i <= $high - 1; i = i + 1)); do
echo -e "\033["$i";1H|"
echo -e "\033["$i";"$width"H|\n"
done
}
- In der Funktion
draw_border()werden zwei Ganzzahlvariablenwidthundhighdeklariert, die jeweils die Breite und Höhe des Terminalfensters darstellen. - Der Befehl
echo -e "\033[37;40m"wird verwendet, um die Anzeigefarbe festzulegen. Geschachteltefor-Schleifen werden verwendet, um jede Zeile und Spalte des Terminals zu durchlaufen und den Hintergrund zu füllen. - Innerhalb der inneren Schleife werden ANSI-Escape-Codes verwendet, um den Cursor an die gewünschten Koordinaten innerhalb des Terminals zu positionieren. Beispielsweise setzt
echo -e "\033["$j";"$i"H"die Cursorposition auf Zeile j und Spalte i. - Nachdem der Terminalhintergrund gefüllt wurde, werden dekorative Rahmen mit bestimmten Zeichen gezeichnet. Die verwendeten Zeichen sind
+,-und|, die üblicherweise zum Zeichnen von Rahmen verwendet werden. Die Positionen dieser Zeichen werden durch ihre Zeilen- und Spaltenwerte bestimmt und mit ANSI-Escape-Codes ausgegeben. Beispielsweise platziertecho -e "\033[1;1H+"das+-Zeichen in der oberen linken Ecke des Terminals.
Zusammenfassend lässt sich sagen, dass die Funktion draw_border() das Terminal leert, die Hintergrundfarbe auf schwarz setzt, das Terminal mit Leerzeichen füllt, um einen Hintergrund zu erstellen. Schließlich zeichnet sie einen optisch ansprechenden Rahmen, indem sie Zeichen (z. B. + für die Ecken, - und | für die Linien) an bestimmten Positionen platziert.

Füllen der Hintergrundfarbe der Tippoberfläche
## Löscht die gesamte Zeichenlandebahn
function clear_all_area() {
local i j
## Füllt den Tippbereich
for ((i = 5; i <= 21; i++)); do
for ((j = 3; j <= 77; j = j + 1)); do
## Setzt die Anzeigeposition
echo -e "\033[44m\033["$i";"$j"H "
done
done
echo -e "\033[37;40m"
}
## Funktion: Löscht eine bestimmte Spalte von Zeichen
## Eingabe: Die zu löschende Spaltennummer
## Rückgabe: Keine
function clear_line() {
local i
## Füllt den Tippbereich
for ((i = 5; i <= 21; i++)); do
for ((j = $1; j <= $1 + 9; j = j + 1)); do
echo -e "\033[44m\033["$i";"$j"H "
done
done
echo -e "\033[37;40m"
}
- Die
clear_all_area()-Funktion:
- Sie wird verwendet, um die gesamte Zeichenlandebahn zu löschen. Ihre Aufgabe besteht darin, die Zeichen in der Zeichenlandebahn mit der Hintergrundfarbe (schwarz) zu füllen, um die Zeichen im Terminalfenster zu löschen.
- Sie verwendet geschachtelte Schleifen, um alle Kombinationen von
iundjzu durchlaufen, wobeiidie Zeile undjdie Spalte darstellt. - Innerhalb der inneren Schleife wird die Cursorposition auf die angegebene Zeile und Spalte mit ANSI-Escape-Codes festgelegt, und dann wird mit
echo -e "\033[44m\033["$i";"$j"H "ein Leerzeichen an dieser Position ausgegeben, wobei die Hintergrundfarbe auf schwarz (Code 44) festgelegt ist. - Nach den Schleifen verwendet die Funktion
echo -e "\033[37;40m", um die Textfarbe und Hintergrundfarbe auf ihre Standardwerte (weißer Text, schwarzer Hintergrund) zurückzusetzen.
- Die
clear_line()-Funktion:
- Diese Funktion wird verwendet, um eine bestimmte Spalte in der Zeichenlandebahn zu löschen. Sie wird normalerweise verwendet, um den Zeichenpfad in dieser Spalte zu löschen, nachdem der Benutzer das richtige Zeichen eingegeben hat.
- Die Funktion nimmt ein Argument
$1entgegen, das die zu löschende Spaltennummer darstellt. - Sie verwendet eine Schleife, um die Zeilen und Spalten in der Zeichenlandebahn zu durchlaufen und den Zeichenpfad dieser Spalte mit der Hintergrundfarbe (schwarz) zu füllen.
- Ähnlich wie
clear_all_area()legt die Funktion die Cursorposition auf die angegebene Zeile und Spalte mit ANSI-Escape-Codes fest und gibt dann mitecho -e "\033[44m\033["$i";"$j"H "ein Leerzeichen an dieser Position aus, wobei die Hintergrundfarbe auf schwarz festgelegt ist. - Schließlich verwendet die Funktion
echo -e "\033[37;40m", um die Textfarbe und Hintergrundfarbe auf ihre Standardwerte zurückzusetzen.
Generieren von zufälligen Buchstaben und Zahlen
## Funktion: Bewegt das Zeichen entlang des Fallpfads.
## Eingabe: Parameter 1: Die aktuelle Zeile des Zeichens (bezogen auf die Zeitdauer seit dem Zeichen).
## Parameter 2: Die aktuelle Spalte des Zeichens.
## Parameter 3: (Unbenutzter Parameter).
## Rückgabe: Keine
function move() {
local locate_row lastloca
locate_row=$(($1 + 5))
## Zeigt das einzugebende Zeichen an.
echo -e "\033[30;44m\033["$locate_row";"$2"H$3\033[37;40m"
if [ "$1" -gt "0" ]; then
lastloca=$(($locate_row - 1))
## Löscht die vorherige Position.
echo -e "\033[30;44m\033["$lastloca";"$2"H \033[37;40m"
fi
}
function putarray() {
local chars
case $1 in
digit)
chars='0123456789'
for ((i = 0; i < 10; i++)); do
array[$i]=${chars:$i:1}
done
;;
char)
chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
for ((i = 0; i < 52; i++)); do
array[$i]=${chars:$i:1}
done
;;
mix)
chars='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
for ((i = 0; i < 62; i++)); do
array[$i]=${chars:$i:1}
done
;;
*) ;;
esac
}
## Funktion: Generiert ein zufälliges Zeichen des entsprechenden Typs, das in ein zufälliges Zeichen umgewandelt werden soll.
## Eingabe: Der zu generierende Zeichentyp.
## Globale Variable: random_char, array[]
## Rückgabe: Keine
function get_random_char() {
local typenum
declare -i typenum=0
case $1 in
digit)
typenum=$(($RANDOM % 10))
;;
char)
typenum=$(($RANDOM % 52))
;;
mix)
typenum=$(($RANDOM % 62))
;;
*) ;;
esac
random_char=${array[$typenum]}
}
Da Shell-Zeichenkettenvariablen nicht direkt Indizierung unterstützen, müssen wir alle Buchstaben und Zahlen in ein indizierbares Array einfügen. Dies ist der Zweck der putarray-Funktion. Die folgende get_random_char-Funktion wird verwendet, um zufällige Buchstaben und Zahlen zu generieren. Sie verwendet die systemseitige Zufallszahlen-Umgebungsvariable RANDOM, um einen zufälligen Index zu erhalten und dann das entsprechende Zeichen aus dem Array zu lesen.
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
}
Hauptcodeablauf
Da der Code für jedes Funktionsmodul bereits vorhanden ist, benötigen wir noch einen Hauptcodeablauf, um diese Funktionen aufzurufen, damit das Programm läuft.
draw_border
dis_welcome
echo -ne "\033[3;30HSpiel starten. J/N : "
read yourchoice
if [ "$yourchoice" == "J" ] || [ "$yourchoice" == "j" ]; then
draw_border
modechoose
else
clear
exit 1
fi
exit 1
Dieser Codeabschnitt erstellt einen Einstiegspunkt für ein Zeichenfallspiel. Zunächst wird ein Willkommensbildschirm und eine Aufforderung zum Starten des Spiels angezeigt. Anschließend wartet das Programm auf die Benutzereingabe, ob das Spiel gestartet werden soll. Wenn der Benutzer das Spiel starten möchte, wird er aufgefordert, den Schwierigkeitsgrad des Spiels auszuwählen. Wenn der Benutzer das Spiel beenden möchte oder eine ungültige Eingabe macht, wird das Spiel nicht gestartet.
Ausführen und Testen
Als nächstes können wir unser Shell-Tippspiel ausführen:
cd ~/project
bash shell_typing_game.sh

Zusammenfassung
Sie haben gerade ein Projekt erstellt, um ein einfaches Tippspiel in der Shell zu entwickeln. Sie können diese Schritte befolgen, um Ihr eigenes Tippspielprojekt zu erstellen und verschiedene Spielmodi und Schwierigkeitsgrade auszuwählen. Dieses Projekt bietet praktische Erfahrungen in der Shell-Skripting und der Entwicklung von terminalbasierten Spielen. Wenn Sie mit einigen der im Kurs verwendeten Befehle nicht vertraut sind, wie z. B. den verschiedenen Ausgaben von echo, der I/O-Umleitung von exec und der Falle zum Abfangen spezieller Signale, können Sie diese öfter üben, um sich mit ihrer Verwendung vertraut zu machen.



