Introdução
Neste projeto, você aprenderá como criar um jogo de digitação simples usando um script de shell. O jogo exibe caracteres aleatórios na tela, e seu objetivo é digitá-los antes que desapareçam. O jogo oferece diferentes modos com níveis de dificuldade variados. Você pode escolher praticar a digitação de números, letras, uma mistura de ambos, ou até mesmo suas próprias palavras personalizadas.
👀 Pré-visualização

🎯 Tarefas
Neste projeto, você aprenderá:
- Como criar um arquivo de projeto e abri-lo em um editor de código
- Como exibir uma interface de boas-vindas usando caracteres especiais e cores
- Como implementar um menu de seleção de modo para escolher o nível de dificuldade
- Como implementar um menu de seleção de categoria de digitação para escolher o tipo de caracteres a serem praticados
- Como criar funções para desenhar uma borda e preencher a cor de fundo da interface de digitação
- Como gerar letras e números aleatórios para o jogo de digitação
- Como implementar a funcionalidade de digitação, incluindo o tratamento da entrada do usuário e o cálculo da precisão
- Como criar uma função de saída elegante para lidar com sinais especiais
🏆 Conquistas
Após concluir este projeto, você será capaz de:
- Demonstrar os fundamentos de scripting de shell
- Usar caracteres especiais e cores na saída do terminal
- Ler a entrada do usuário em scripts de shell
- Implementar menus e interfaces de usuário em scripts de shell
- Lidar com sinais especiais em scripts de shell
Criar Arquivo do Projeto
Para começar, por favor, crie um novo arquivo chamado shell_typing_game.sh e abra-o no seu editor de código preferido.
cd ~/project
touch shell_typing_game.sh
Exibir Interface de Boas-Vindas
#!/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
A longa string no início 010101... é a interface de boas-vindas do jogo a ser exibida. Se você olhar para ela diretamente, pode não entender o que é. Então, você pode copiar esta string para um editor gedit e, em seguida, pressionar o atalho Ctrl+F para encontrar. Quando você digitar 1, você entenderá imediatamente. Vamos nos concentrar no comando echo. Precisamos usar o comando echo porque queremos exibi-lo na tela (aqui se refere ao terminal Xfce. A entrada padrão, a saída e a saída de erro padrão no Linux são todas conectadas ao terminal por padrão). Para obter algumas funções especiais, como imprimir em uma posição especificada e definir a cor do caractere de exibição, e imprimir o fundo de todo o jogo, precisamos usar alguns parâmetros especiais do comando correspondente, conforme mostrado no código a seguir:
## Set the color and cursor start position.
echo -ne "\033[37;40m\033[5;3H"
Com base nos comentários, podemos entender aproximadamente sua função. Mas o que isso significa exatamente? Primeiro, o parâmetro -n significa imprimir na linha atual sem quebra de linha após a saída. Em seguida, defina a cor do caractere de saída e a posição do cursor. Entre eles, \033[cor do primeiro plano;cor do fundo m, e o seguinte é \033[linha;coluna H. No entanto, para fazer com que echo reconheça essas sequências de escape especiais, precisamos do parâmetro -e, caso contrário, echo irá gerar esses caracteres como estão.
Cores do primeiro plano: 30-39, cores do fundo: 40-49.
| Valor | Cores do primeiro plano | Valor | Faixa de cores do fundo: 40 - 49 |
|---|---|---|---|
| 30 | Definir primeiro plano preto | 40 | Definir fundo preto |
| 31 | Definir primeiro plano vermelho | 41 | Definir fundo vermelho |
| 32 | Definir primeiro plano verde | 42 | Definir fundo verde |
| 33 | Definir primeiro plano marrom | 43 | Definir fundo marrom |
| 34 | Definir primeiro plano azul | 44 | Definir fundo azul |
| 35 | Definir primeiro plano roxo | 45 | Definir fundo roxo |
| 36 | Definir primeiro plano ciano | 46 | Definir fundo ciano |
| 37 | Definir primeiro plano branco | 47 | Definir fundo branco |
| 38 | Definir sublinhado na cor padrão do primeiro plano | 49 | Definir fundo preto padrão |
| 39 | Definir sublinhado desligado na cor padrão do primeiro plano |
Não esperava que echo fosse usado assim! Você sente que se reapresentou a ele? Ok, agora vamos testar este trecho de código no terminal.
cd ~/project
bash shell_typing_game.sh

Exibir Menu de Seleção de Modo
## Declare the variable 'time' to represent the typing timeout, where different difficulty levels correspond to different timeout values
declare -i time
function modechoose() {
## Select from three different modes
echo -e "\033[8;30H1) easy mode"
echo -e "\033[9;30H2) normal mode"
echo -e "\033[10;30H3) difficult mode"
echo -ne "\033[22;2HPlease input your choice: "
read mode
case $mode in
"1")
time=10
dismenu ## Call the menu selection function
;;
"2")
time=5
dismenu
;;
"3")
time=3
dismenu
;;
*)
echo -e "\033[22;2HYour choice is incorrect, please try again"
sleep 1
;;
esac
}
Antes de definir esta função, primeiro declaramos uma variável inteira 'time' usando o comando declare -i. Em seguida, na instrução case a seguir, definimos valores diferentes para a variável 'time' com base na seleção de dificuldade do usuário. Quanto maior a dificuldade, menor o valor. Essa variável é realmente usada na função de processamento de digitação subsequente. Você pode se lembrar que, em scripts de shell, variáveis declaradas ou definidas dentro ou fora de uma função são tratadas como variáveis globais com um escopo em todo o arquivo de script, a menos que a palavra-chave 'local' seja usada para declarar a variável dentro de uma função. Quanto ao efeito de exibição do menu, ainda usamos o comando echo e, em seguida, usamos o comando read para ler a entrada do usuário na variável 'mode'.

Exibir Menu de Seleção de Categoria de Digitação
function display_menu() {
while [ 1 ]; do
draw_border
echo -e "\033[8;30H1) Praticar digitação de números"
echo -e "\033[9;30H2) Praticar digitação de letras"
echo -e "\033[10;30H3) Praticar digitação de caracteres alfanuméricos"
echo -e "\033[11;30H4) Praticar digitação de palavras"
echo -e "\033[12;30H5) Sair"
echo -ne "\033[22;2HPlease input your choice: "
read choice
case $choice in
"1")
draw_border
## The next two are function parameters, the first parameter indicates the typing type, and the second is the function for moving characters
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 "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
;;
"5" | "q" | "Q")
draw_border
echo -e "\033[10;25HYou will exit this game now"
echo -e "\033[39;49m"
sleep 1
clear
exit 1
;;
*)
draw_border
echo -e "\033[22;2HYour choice is wrong, please try again"
sleep 1
;;
esac
done
}
Nesta função, vamos primeiro explicar as duas funções chamadas no ramo case. Primeiro, a função draw_border é usada para desenhar a borda da interface de digitação, que será mostrada mais tarde. Em seguida, a função main é chamada. Esta função main não tem nenhum papel especial como a função main em linguagens como Java e C, ela simplesmente tem o nome main, que é usado para indicar que ela desempenha um papel importante em todo o programa. Você pode ter notado que há um argumento após cada main, sim, este é o parâmetro passado para a função. Em shell, os parâmetros não são escritos entre parênteses imediatamente após o nome da função, ao contrário de muitas outras linguagens. Deve-se notar também que no quarto ramo da instrução case, que são estas linhas:
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
De acordo com o prompt do menu, podemos saber que este ramo é para praticar a digitação de palavras. Este programa permite que os usuários usem arquivos de palavras personalizados (arquivos de texto com um determinado requisito de formatação, com uma palavra por linha, você pode baixar um arquivo de lista de palavras online e usar o comando awk para extrair) para a prática. Então, primeiro precisamos ler um nome de arquivo de entrada do usuário para selecionar o arquivo de palavras. Em seguida, usamos o comando exec para criar um pipeline que aponta para o arquivo, ou seja, redirecionando a saída do arquivo para o descritor de arquivo 4 (fd). Como exec é um comando embutido no bash, você não poderá ver a documentação de exec usando o comando man. O redirecionamento de E/S com exec geralmente está relacionado a fd, e o shell geralmente tem 10 fd, variando de 0 a 9. Os fd comumente usados são 3, que são 0 (stdin, entrada padrão), 1 (stdout, saída padrão) e 2 (stderr, saída de erro padrão). Apenas entenda seu significado por enquanto.

Desenhando uma Borda para uma Interface de Digitação
function draw_border() {
declare -i width
declare -i high
width=79 ## terminal default width - 1
high=23 ## terminal default height - 1
clear
## Set display color to white on black background
echo -e "\033[37;40m"
## Set background color
for ((i = 1; i <= $width; i = i + 1)); do
for ((j = 1; j <= $high; j = j + 1)); do
## Set display position
echo -e "\033["$j";"$i"H "
done
done
## Draw background border
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
}
- Na função
draw_border(), duas variáveis inteiraswidthehighsão declaradas, que representam a largura e a altura da janela do terminal, respectivamente. - O comando
echo -e "\033[37;40m"é usado para definir a cor da exibição. Loopsforaninhados são usados para iterar por cada linha e coluna do terminal para preencher o fundo. - Dentro do loop interno, códigos de escape ANSI são usados para posicionar o cursor nas coordenadas desejadas dentro do terminal. Por exemplo,
echo -e "\033["$j";"$i"H"define a posição do cursor para a linha j e a coluna i. - Após preencher o fundo do terminal, bordas decorativas são desenhadas usando caracteres específicos. Os caracteres usados incluem
+,-e|, que são comumente usados para desenhar bordas. As posições desses caracteres são determinadas por seus valores de linha e coluna, e impressas usando códigos de escape ANSI. Por exemplo,echo -e "\033[1;1H+"coloca o caractere+no canto superior esquerdo do terminal.
Em resumo, a função draw_border() limpa o terminal, define a cor do fundo para preto, preenche o terminal com espaços para criar um fundo. Finalmente, ela desenha uma borda visualmente atraente, colocando caracteres (como + para cantos, - e | para linhas) em posições específicas.

Preenchendo a Cor de Fundo da Interface de Digitação
## Clear the entire character landing area
function clear_all_area() {
local i j
## Fill the typing area
for ((i = 5; i <= 21; i++)); do
for ((j = 3; j <= 77; j = j + 1)); do
## Set the display position
echo -e "\033[44m\033["$i";"$j"H "
done
done
echo -e "\033[37;40m"
}
## Function: Clear a specific column of characters
## Input: The column number to be cleared
## Return: None
function clear_line() {
local i
## Fill the typing area
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"
}
- A função
clear_all_area():
- É usada para limpar toda a área de aterrissagem de caracteres. Sua função é preencher os caracteres na área de aterrissagem de caracteres com a cor de fundo (preto) para limpar os caracteres na janela do terminal.
- Ela usa loops aninhados para iterar sobre todas as combinações de
iej, ondeirepresenta a linha ejrepresenta a coluna. - Dentro do loop interno, ela define a posição do cursor para a linha e coluna especificadas usando códigos de escape ANSI e, em seguida, usa
echo -e "\033[44m\033["$i";"$j"H "para imprimir um caractere de espaço nessa posição, mas com a cor de fundo definida como preto (código 44). - Após os loops, a função usa
echo -e "\033[37;40m"para restaurar a cor do texto e a cor de fundo para seus valores padrão (texto branco, fundo preto).
- A função
clear_line():
- Esta função é usada para limpar uma coluna específica na área de aterrissagem de caracteres. Ela é tipicamente usada para limpar o caminho do caractere naquela coluna após o usuário inserir o caractere correto.
- A função recebe um argumento
$1, que representa o número da coluna a ser limpa. - Ela usa um loop para iterar sobre as linhas e colunas na área de aterrissagem de caracteres e preenche o caminho do caractere daquela coluna com a cor de fundo (preto).
- Semelhante a
clear_all_area(), a função define a posição do cursor para a linha e coluna especificadas usando códigos de escape ANSI e, em seguida, usaecho -e "\033[44m\033["$i";"$j"H "para imprimir um caractere de espaço nessa posição, mas com a cor de fundo definida como preto. - Finalmente, a função usa
echo -e "\033[37;40m"para restaurar a cor do texto e a cor de fundo para seus valores padrão.
Gerando Letras e Números Aleatórios
## Function: Move the character along the falling path.
## Input: Parameter 1: The current row of the character (related to the time length away from the character).
## Parameter 2: The current column of the character.
## Parameter 3: (Unused parameter).
## Return: None
function move() {
local locate_row lastloca
locate_row=$(($1 + 5))
## Display the character to be input.
echo -e "\033[30;44m\033["$locate_row";"$2"H$3\033[37;40m"
if [ "$1" -gt "0" ]; then
lastloca=$(($locate_row - 1))
## Clear the previous 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
}
## Function: Generate a random character of the corresponding type to be converted into a random character.
## Input: The type of character to be generated.
## Global Var: random_char, array[]
## Return: None
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]}
}
Devido ao fato de que as variáveis de string do shell não suportam diretamente a indexação, precisamos colocar todas as letras e números em um array indexado. Este é o propósito da função putarray. A função get_random_char abaixo é usada para gerar letras e números aleatórios. Ela usa a variável de ambiente de número aleatório do sistema RANDOM para obter um índice aleatório e, em seguida, lê o caractere correspondente do array.
Implementação da Digitação
Após todos os preparativos, finalmente podemos começar a implementar a função de digitação. Vamos dar uma olhada no código a seguir:
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
## Store the corresponding characters into an array, $1 represents the user-selected character type
putarray $1
## Initialize the game start time
gamestarttime=$(date +%s)
while [ 1 ]; do
echo -e "\033[2;2H Please enter the letter on the screen before it disappears!"
echo -e "\033[3;2H Game time: "
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 Accuracy: \033[31;40m$accuracy %\033[37;40m"
echo -ne "\033[22;2H Your input: "
clear_all_area
## Loop 10 times to check if a column of characters times out or is hit
for ((line = 20; line <= 60; line = line + 10)); do
## Check if the column of characters has been hit
if [ "${ifchar[$line]}" == "" ] || [ "${donetime[$line]}" -gt "$time" ]; then
## Clear the display in that column
clear_line $line
## Generate a random character
if [ "$1" == "word" ]; then
read -u 4 word
if [ "$word" == "" ]; then ## End of file reading
exec 4< $file
fi
putchar[$line]=$word
else
get_random_char $1
putchar[$line]=$random_char
fi
numtotal=$numtotal+1
## Set the flag to 1
ifchar[$line]=1
## Reset the timer
starttime[$line]=$(date +%s)
curtime[$line]=${starttime[$line]}
donetime[$line]=$time
## Reset the column position to 0
column[$line]=0
if [ "$1" == "word" ]; then
move 0 $line ${putchar[$line]}
fi
else
## If it has not timed out or been hit, update the timer and current 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" ## Clear the input line characters
## Check user input characters and act as a one-second timer
if read -n 1 -t 0.5 tmp; then
## Successful input, loop checks if the input matches a column
for ((line = 20; line <= 60; line = line + 10)); do
if [ "$tmp" == "${putchar[$line]}" ]; then
## Clear the display in that column
clear_line $line
## If matched, clear the flag
ifchar[$line]=""
echo -e "\007\033[32;40m\033[4;62H right !\033[37;40m"
numright=$numright+1
## Exit the loop
break
else
## Otherwise, always display an error until timeout
echo -e "\033[31;40m\033[4;62Hwrong,try again!\033[37;40m"
fi
done
fi
else
echo -ne "\033[22;14H" ## Clear the input line characters
## Check user input characters and act as a timer
if read tmp; then
## Successful input, loop checks if the input matches a column
for ((line = 20; line <= 60; line = line + 10)); do
if [ "$tmp" == "${putchar[$line]}" ]; then
## Clear the display in that column
clear_line $line
## If matched, clear the flag
ifchar[$line]=""
echo -e "\007\033[32;40m\033[4;62H right !\033[37;40m"
numright=$numright+1
## Exit the loop
break
else
## Otherwise, always display an error until timeout
echo -e "\033[31;40m\033[4;62Hwrong,try again!\033[37;40m"
fi
done
fi
fi
trap " doexit " 2 ## Capture special signals
## Calculate accuracy
accuracy=$numright*100/$numtotal
done
}
Agora, vamos explicar o método de digitação de caracteres e palavras individualmente, pois eles são ligeiramente diferentes.
Para digitar caracteres individuais, queremos que cinco caracteres apareçam ao mesmo tempo, e eles continuarão a cair em um intervalo de tempo definido até que desapareçam quando atingirem uma certa altura (determinada pelo período de tempo limite escolhido no início). Se eles não forem atingidos (o usuário não inserir o caractere correto correspondente) antes de desaparecerem, um novo caractere aparecerá naquela coluna. Portanto, primeiro, fazemos um loop para inicializar uma sequência de caracteres e armazená-la em um array. O índice do array representa o número da coluna do terminal, o que tem a vantagem de gerenciar independentemente cada coluna de caracteres, se ela expirou ou foi atingida, e onde ela deve ser colocada. A desvantagem é que criaremos um array relativamente grande, mas não será tão grande que não possa ser tratado. Isso não importa para o shell, pois ele não aloca memória com base no tamanho do índice do array. O primeiro loop for é responsável por isso, e ele também verifica no final de cada loop principal se uma coluna está vazia e, em seguida, um novo caractere aparecerá naquela coluna.
Em seguida, há uma longa instrução if...else. Você se lembra do parâmetro passado ao chamar a função main? A parte mais externa é usada para distinguir se deve digitar caracteres individuais ou palavras. Vamos primeiro olhar para a digitação de caracteres individuais, que contém uma linha crucial:
if read -n 1 -t 0.5 tmp
O parâmetro -n do comando read especifica o comprimento dos caracteres a serem lidos. Aqui, um comprimento de 1 é especificado, o que significa que a entrada termina imediatamente após o usuário inserir um caractere, sem a necessidade de pressionar a tecla Enter. O parâmetro -t especifica o tempo limite de entrada. Se o usuário não inserir ou a entrada não for concluída dentro do período de tempo limite, a leitura terminará imediatamente. Como o comando read é uma operação síncrona para o mesmo shell ao ler a entrada do usuário, a implementação da queda do caractere depende desse tempo limite (implementando indiretamente um atraso na queda). Aqui, 0.5s é definido porque a maioria dos usuários pode concluir a entrada de um caractere em 0.5s. Após ler a entrada do usuário, ele usa um loop for para comparar cada array de caracteres correspondente a cada coluna e, se houver uma correspondência, ele chama uma função clear_line para limpar o caractere na coluna atual e define a variável de sinalizador ifchar[$line] como 0, o que indica que foi limpo.
O fluxo de digitação de palavras é basicamente o mesmo que o de digitação de caracteres, exceto que, como não podemos estimar o tempo que o usuário leva para inserir uma palavra com um comprimento indefinido, não podemos definir o tempo limite de entrada para o comando read. Sem esse tempo limite, a implementação final da digitação de palavras pode não cair automaticamente como ao digitar caracteres, mas, em vez disso, a palavra cai uma linha depois que inserimos uma palavra e pressionamos Enter. Claro, você pode considerar o uso de outros métodos para obter os mesmos ou melhores efeitos que a digitação de caracteres.
Além disso, o tratamento da obtenção de novas palavras para cada coluna é ligeiramente diferente porque estamos lendo de um arquivo, em vez de gerar palavras aleatórias. Você ainda se lembra do descritor de arquivo 4 que criamos anteriormente, que aponta para um arquivo de palavras? Usamos este descritor de arquivo aqui:
read -u 4 word
if [ "$word" == "" ]; then ## End of file reading
exec 4< $file
fi
putchar[$line]=$word
Aqui, ainda usamos o comando read e, com o parâmetro -u, podemos especificar de qual descritor de arquivo ler o arquivo linha por linha na variável. A instrução de verificação vazia subsequente é para recriar o descritor de arquivo que aponta para o arquivo quando um arquivo atinge seu fim, para que read possa ler do início do arquivo novamente.
Você acha que é um pouco complicado? Não se preocupe, ainda não terminamos. Em seguida, preste atenção na penúltima linha da função main:
trap " doexit " 2 ## Capture special signals
O comando trap é usado para capturar sinais especiais no shell, como Ctrl+C, Ctrl+D, Ctrl+Z, ESC, etc. Originalmente, esses sinais especiais são tratados pelo próprio shell. Como queremos que o jogo saia de forma mais elegante quando ele sair, podemos interceptar o segundo sinal especial, que é Ctrl+C, para implementar algum processamento personalizado. Por exemplo, aqui, após capturar o segundo sinal, ele chama a função doexit abaixo para sair do programa de forma elegante:
function doexit() {
draw_border
echo -e "\033[10;30Hthis game will exit....."
echo -e "\033[0m"
sleep 2
clear
exit 1
}
Fluxo Principal do Código
Como o código para cada módulo de função já está em vigor, para fazer o programa rodar, ainda precisamos de um fluxo de código principal para chamar essas funções.
draw_border
dis_welcome
echo -ne "\033[3;30Hstart the game. Y/N : "
read yourchoice
if [ "$yourchoice" == "Y" ] || [ "$yourchoice" == "y" ]; then
draw_border
modechoose
else
clear
exit 1
fi
exit 1
Este trecho de código cria um ponto de entrada para um jogo de queda de caracteres. Ele primeiro exibe uma tela de boas-vindas e um prompt para iniciar o jogo, depois espera pela entrada do usuário sobre se deve iniciar o jogo. Se o usuário escolher iniciar o jogo, ele será solicitado a escolher o modo de dificuldade do jogo. Se o usuário escolher sair ou inserir uma entrada inválida, o jogo não será iniciado.
Execução e Testes
Em seguida, podemos executar nosso jogo de digitação em shell:
cd ~/project
bash shell_typing_game.sh

Resumo
Você acabou de criar um projeto para construir um jogo de digitação simples no shell. Você pode seguir estas etapas para criar seu próprio projeto de jogo de digitação e escolher diferentes modos de jogo e níveis de dificuldade. Este projeto fornece experiência prática em shell scripting e desenvolvimento de jogos baseados em terminal. Se você não estiver familiarizado com alguns dos comandos usados no curso, como as várias saídas de echo, o redirecionamento de E/S de exec e a armadilha (trap) para capturar sinais especiais, você pode praticá-los mais para se familiarizar com seu uso.



