타이핑 기능 구현
모든 준비가 완료되었으므로, 이제 타이핑 기능을 구현할 수 있습니다. 다음 코드를 살펴보겠습니다.
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
}
이제 단일 문자 및 단어 타이핑 방식을 별도로 설명하겠습니다. 약간의 차이가 있기 때문입니다.
단일 문자 타이핑의 경우, 다섯 개의 문자가 동시에 나타나고, 설정된 시간 간격으로 계속 떨어지다가 특정 높이에 도달하면 사라지도록 (처음에 선택한 타임아웃 기간에 의해 결정됨) 합니다. 사라지기 전에 맞히지 못하면 (사용자가 해당 올바른 문자를 입력하지 않음), 해당 열에 새 문자가 나타납니다. 따라서 먼저, 문자 시퀀스를 초기화하고 배열에 저장하기 위해 루프를 실행합니다. 배열의 인덱스는 터미널의 열 번호를 나타내며, 이는 각 열의 문자를 독립적으로 관리하는 이점이 있습니다. 타임아웃되었는지, 맞았는지, 어디에 배치해야 하는지 등입니다. 단점은 비교적 큰 배열을 생성한다는 것이지만, 처리할 수 없을 정도로 크지는 않습니다. 쉘에서는 배열 인덱스의 크기에 따라 메모리를 할당하지 않으므로 이는 중요하지 않습니다. 첫 번째 for 루프가 이를 담당하며, 각 주요 루프의 끝에서 열이 비어 있는지 확인한 다음 해당 열에 새 문자가 나타납니다.
다음은 긴 if...else 문입니다. main 함수를 호출할 때 전달된 매개변수를 기억하십니까? 가장 바깥쪽 부분은 단일 문자를 타이핑할지, 단어를 타이핑할지를 구분하는 데 사용됩니다. 먼저 단일 문자 타이핑을 살펴보겠습니다. 여기에는 중요한 줄이 포함되어 있습니다.
if read -n 1 -t 0.5 tmp
read 명령의 -n 매개변수는 읽을 문자의 길이를 지정합니다. 여기서는 1의 길이가 지정되어 사용자가 한 문자를 입력하면 Enter 키를 누를 필요 없이 입력을 즉시 종료합니다. -t 매개변수는 입력 타임아웃 시간을 지정합니다. 사용자가 입력하지 않거나 입력이 타임아웃 기간 내에 완료되지 않으면 읽기가 즉시 종료됩니다. read 명령은 사용자 입력을 읽을 때 동일한 쉘에 대한 동기식 작업이므로, 문자 낙하는 이 타임아웃에 의존합니다 (간접적으로 낙하 지연을 구현). 여기서는 대부분의 사용자가 0.5s 내에 한 문자의 입력을 완료할 수 있으므로 0.5s로 설정됩니다. 사용자 입력을 읽은 후, 각 열에 해당하는 각 문자 배열을 비교하기 위해 for 루프를 사용하고, 일치하는 항목이 있으면 clear_line 함수를 호출하여 현재 열의 문자를 지우고, 플래그 변수 ifchar[$line]을 0으로 설정하여 지워졌음을 나타냅니다.
단어 타이핑의 흐름은 기본적으로 문자 타이핑과 동일합니다. 단, 무한한 길이의 단어를 입력하는 데 걸리는 시간을 추정할 수 없으므로, read 명령에 대한 입력 타임아웃 시간을 설정할 수 없습니다. 이 타임아웃이 없으면, 단어 타이핑의 최종 구현은 문자 타이핑과 같이 자동으로 떨어지지 않을 수 있으며, 대신 단어를 입력하고 Enter 키를 누르면 단어가 한 줄 아래로 떨어집니다. 물론, 문자 타이핑과 동일하거나 더 나은 효과를 얻기 위해 다른 방법을 고려할 수 있습니다.
또한, 각 열에 대한 새 단어를 얻는 처리가 약간 다릅니다. 무작위 단어를 생성하는 대신 파일에서 읽기 때문입니다. 이전에 생성한 파일 디스크립터 4를 기억하십니까? 이는 단어 파일을 가리킵니다. 여기에서 이 파일 디스크립터를 사용했습니다.
read -u 4 word
if [ "$word" == "" ]; then ## End of file reading
exec 4< $file
fi
putchar[$line]=$word
여기서도 read 명령을 사용하며, -u 매개변수를 사용하면 파일에서 줄 단위로 변수로 읽을 파일 디스크립터를 지정할 수 있습니다. 후속 빈 확인 문은 파일이 끝에 도달했을 때 파일을 가리키는 파일 디스크립터를 다시 생성하여 read가 파일의 처음부터 다시 읽을 수 있도록 합니다.
약간 복잡하다고 느끼십니까? 걱정하지 마세요. 아직 끝나지 않았습니다. 다음으로, main 함수의 마지막 줄에서 두 번째 줄에 주의하십시오.
trap " doexit " 2 ## Capture special signals
trap 명령은 쉘에서 Ctrl+C, Ctrl+D, Ctrl+Z, ESC 등과 같은 특수 신호를 캡처하는 데 사용됩니다. 원래 이러한 특수 신호는 쉘 자체에서 처리됩니다. 게임이 종료될 때 더 깔끔하게 종료되기를 원하므로, 2번째 특수 신호인 Ctrl+C를 가로채서 일부 사용자 정의 처리를 구현할 수 있습니다. 예를 들어, 여기서는 2번째 신호를 캡처한 후 아래의 doexit 함수를 호출하여 프로그램을 깔끔하게 종료합니다.
function doexit() {
draw_border
echo -e "\033[10;30Hthis game will exit....."
echo -e "\033[0m"
sleep 2
clear
exit 1
}