Создание игры по печати с использованием Bash

LinuxBeginner
Практиковаться сейчас

Введение

В этом проекте вы научитесь создавать простую игру для тренировки печати с использованием shell-скрипта. Игра отображает случайные символы на экране, и ваша цель - ввести их до того, как они исчезнут. Игра предлагает различные режимы с разным уровнем сложности. Вы можете выбрать тренировку печати цифр, букв, их смеси или даже своих собственных слов.

👀 Предпросмотр

Shell Typing Game

🎯 Задачи

В этом проекте вы научитесь:

  • Создавать проектный файл и открывать его в редакторе кода
  • Отображать приветственный интерфейс с использованием специальных символов и цветов
  • Реализовывать меню выбора режима для выбора уровня сложности
  • Реализовывать меню выбора категории печати для выбора типа символов для тренировки
  • Создавать функции для рисования рамки и заполнения фонового цвета интерфейса печати
  • Генерировать случайные буквы и цифры для игры в печать
  • Реализовывать функциональность печати, включая обработку пользовательского ввода и расчет точности
  • Создавать функцию graceful exit для обработки специальных сигналов

🏆 Достижения

После завершения этого проекта вы сможете:

  • Демонстрировать основы shell-скриптинга
  • Использовать специальные символы и цвета в терминальном выводе
  • Читать ввод от пользователя в shell-скриптах
  • Реализовывать меню и пользовательские интерфейсы в shell-скриптах
  • Обрабатывать специальные сигналы в shell-скриптах

Создать проектный файл

Для начала создайте новый файл с именем shell_typing_game.sh и откройте его в вашем предпочтительном редакторе кода.

cd ~/project
touch shell_typing_game.sh

Отобразить приветственный интерфейс

#!/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

Длинная строка в начале 010101... представляет собой приветственный интерфейс игры, который нужно отобразить. Если вы посмотрите на нее напрямую, вам может быть непонятно, что это такое. Поэтому вы можете скопировать эту строку в редактор gedit, а затем нажать сочетание клавиш Ctrl+F для поиска. Когда вы введете 1, все сразу станет понятно. Теперь сосредоточимся на команде echo. Мы должны использовать команду echo, так как хотим отобразить интерфейс на экране (здесь имеется в виду терминал Xfce. Стандартный ввод, вывод и стандартный вывод ошибок в Linux по умолчанию подключены к терминалу). Чтобы реализовать некоторые специальные функции, такие как печать в заданной позиции и установка цвета отображаемых символов, а также напечатать фон всей игры, нам нужно использовать некоторые специальные параметры соответствующих команд, как показано в следующем коде:

## Set the color and cursor start position.
echo -ne "\033[37;40m\033[5;3H"

По комментариям мы можем приблизительно понять, какую функцию выполняет этот код. Но что же это значит на самом деле? Во - первых, параметр -n означает, что вывод будет осуществлен в текущей строке без перевода строки после вывода. Затем устанавливается цвет выводимых символов и позиция курсора. Здесь \033[цвет переднего плана;цвет фона m, а далее \033[строка;столбец H. Однако, чтобы echo распознавал эти специальные escape - последовательности, нам нужен параметр -e, в противном случае echo выведет эти символы как есть.

Цвета переднего плана: 30 - 39, цвета фона: 40 - 49.

Значение Цвета переднего плана Значение Диапазон цветов фона: 40 - 49
30 Установить черный цвет переднего плана 40 Установить черный цвет фона
31 Установить красный цвет переднего плана 41 Установить красный цвет фона
32 Установить зеленый цвет переднего плана 42 Установить зеленый цвет фона
33 Установить коричневый цвет переднего плана 43 Установить коричневый цвет фона
34 Установить синий цвет переднего плана 44 Установить синий цвет фона
35 Установить фиолетовый цвет переднего плана 45 Установить фиолетовый цвет фона
36 Установить голубой цвет переднего плана 46 Установить голубой цвет фона
37 Установить белый цвет переднего плана 47 Установить белый цвет фона
38 Включить подчеркивание на цвете переднего плана по умолчанию 49 Установить черный цвет фона по умолчанию
39 Выключить подчеркивание на цвете переднего плана по умолчанию

Не ожидали, что echo можно использовать таким образом! Не чувствуете ли, что вы вновь познакомились с этой командой? Хорошо, теперь давайте протестируем этот фрагмент кода в терминале.

cd ~/project
bash shell_typing_game.sh

Shell Typing Game

Отобразить меню выбора режима

## 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
}

Перед определением этой функции мы сначала объявили целочисленную переменную 'time' с помощью команды declare -i. Затем в следующем операторе case мы установили разные значения для переменной 'time' в зависимости от выбора уровня сложности пользователем. Чем выше уровень сложности, тем меньше значение. Эта переменная фактически используется в последующей функции обработки печати. Вы, возможно, помните, что в shell - скриптах переменные, объявленные или определенные внутри или вне функции, рассматриваются как глобальные переменные с областью действия на весь файл скрипта, если только не используется ключевое слово 'local' для объявления переменной внутри функции. Что касается эффекта отображения меню, мы по - прежнему используем команду echo, а затем используем команду read для чтения ввода пользователя в переменную 'mode'.

Shell Typing Game

Отобразить меню выбора категории печати

function display_menu() {
  while [ 1 ]; do
    draw_border
    echo -e "\033[8;30H1) Practice typing numbers"
    echo -e "\033[9;30H2) Practice typing letters"
    echo -e "\033[10;30H3) Practice typing alphanumeric characters"
    echo -e "\033[11;30H4) Practice typing words"
    echo -e "\033[12;30H5) Quit"
    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
}

В этой функции давайте сначала объясним две функции, вызываемые в ветках case. Во - первых, функция draw_border используется для рисования рамки интерфейса печати, которая будет показана позже. Затем вызывается функция main. Эта функция main не имеет особой роли, как функция main в таких языках, как Java и C, она просто имеет имя main, которое используется для указания на то, что она играет основную роль в целом программе. Возможно, вы заметили, что после каждой main есть аргумент. Да, это параметр, передаваемый в функцию. В shell параметры не записываются в скобках сразу после имени функции, в отличие от многих других языков. Также следует отметить, что в четвертой ветке оператора case, то есть в этих строках:

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

Согласно подсказке меню, мы знаем, что эта ветка предназначена для тренировки печати слов. Эта программа позволяет пользователям использовать свои собственные файлы со словами (текстовые файлы с определенными требованиями к форматированию, по одному слову в каждой строке. Вы можете скачать файл со списком слов в Интернете и использовать команду awk для извлечения) для тренировки. Поэтому сначала нам нужно прочитать имя файла, введенное пользователем, чтобы выбрать файл со словами. Затем мы используем команду exec для создания канала, который указывает на файл, то есть перенаправляем вывод файла на дескриптор файла 4 (fd). Поскольку exec является встроенной командой в bash, вы не сможете увидеть документацию по exec с помощью команды man. Перенаправление ввода - вывода с использованием exec обычно связано с fd, и в shell обычно есть 10 fd, от 0 до 9. Часто используемые fd - это 0 (stdin, стандартный ввод), 1 (stdout, стандартный вывод) и 2 (stderr, стандартный вывод ошибок). Пока просто поймите его смысл.

Shell Typing Game

Рисование рамки для интерфейса печати

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
}
  • В функции draw_border() объявляются две целочисленные переменные width и high, которые представляют ширину и высоту окна терминала соответственно.
  • Команда echo -e "\033[37;40m" используется для установки цвета отображения. Вложенные циклы for используются для перебора каждой строки и столбца терминала для заполнения фона.
  • Внутри внутреннего цикла используются ANSI - escape последовательности для позиционирования курсора в нужных координатах терминала. Например, echo -e "\033["$j";"$i"H" устанавливает позицию курсора в строке j и столбце i.
  • После заполнения фона терминала рисуются декоративные рамки с использованием специальных символов. Используемые символы включают +, - и |, которые обычно используются для рисования рамок. Позиции этих символов определяются их значениями строки и столбца и выводятся с использованием ANSI - escape последовательностей. Например, echo -e "\033[1;1H+" размещает символ + в верхнем левом углу терминала.

В целом, функция draw_border() очищает терминал, устанавливает цвет фона в черный, заполняет терминал пробелами для создания фона. Наконец, она рисует визуально привлекательную рамку, размещая символы (например, + для углов, - и | для линий) в определенных позициях.

Shell Typing Game

Заливка фонового цвета интерфейса печати

## 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"
}
  1. Функция clear_all_area():
  • Она используется для очистки всей области, где появляются символы. Ее задача - заполнить символы в этой области фоновым цветом (черным), чтобы очистить символы в окне терминала.
  • В ней используются вложенные циклы для перебора всех комбинаций i и j, где i представляет строку, а j - столбец.
  • Внутри внутреннего цикла курсор устанавливается в указанную строку и столбец с использованием ANSI - escape последовательностей, а затем с помощью echo -e "\033[44m\033["$i";"$j"H " выводится пробельный символ в этой позиции, при этом фон устанавливается в черный цвет (код 44).
  • После выполнения циклов функция использует echo -e "\033[37;40m" для восстановления цветов текста и фона до значений по умолчанию (белый текст на черном фоне).
  1. Функция clear_line():
  • Эта функция используется для очистки определенного столбца в области, где появляются символы. Обычно она применяется для очистки пути символов в этом столбце после того, как пользователь ввел правильный символ.
  • Функция принимает один аргумент $1, который представляет номер столбца, который нужно очистить.
  • В ней используется цикл для перебора строк и столбцов в области, где появляются символы, и заполнения пути символов этого столбца фоновым цветом (черным).
  • Как и в функции clear_all_area(), курсор устанавливается в указанную строку и столбец с использованием ANSI - escape последовательностей, а затем с помощью echo -e "\033[44m\033["$i";"$j"H " выводится пробельный символ в этой позиции, при этом фон устанавливается в черный цвет.
  • Наконец, функция использует echo -e "\033[37;40m" для восстановления цветов текста и фона до значений по умолчанию.

Генерация случайных букв и цифр

## 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]}
}

Поскольку строковые переменные в shell не поддерживают прямого индексирования, нам нужно поместить все буквы и цифры в индексируемый массив. Именно для этого служит функция putarray. Функция get_random_char ниже используется для генерации случайных букв и цифр. Она использует системную переменную окружения для случайных чисел RANDOM для получения случайного индекса, а затем считывает соответствующий символ из массива.

Реализация печати

После всех подготовительных этапов мы наконец можем приступить к реализации функции печати. Давайте посмотрим на следующий код:

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
}

Теперь давайте отдельно разберем метод печати отдельных символов и слов, так как они несколько отличаются.

При печати отдельных символов мы хотим, чтобы одновременно появлялось пять символов, и они непрерывно падали с заданным интервалом времени до тех пор, пока не исчезнут, достигнув определенной высоты (определяемой временем истечения, выбранным в начале). Если они не были "попаданы" (пользователь не ввел соответствующий правильный символ) до исчезновения, в этом столбце появится новый символ. Поэтому сначала мы проходим в цикле по последовательности символов и сохраняем их в массиве. Индекс массива представляет номер столбца терминала, что позволяет независимо управлять каждым столбцом символов, отслеживать, истекло ли время или был ли он "попадан", и где он должен быть расположен. Недостаток этого подхода в том, что мы создаем относительно большой массив, но он не настолько велик, чтобы быть неуправляемым. Для shell это не имеет значения, так как он не выделяет память на основе размера индекса массива. Первый цикл for отвечает за это, и он также проверяет в конце каждого основного цикла, пуст ли столбец, и если да, то в нем появляется новый символ.

Далее следует длинное выражение if...else. Помните ли вы параметр, переданный при вызове функции main? Самая внешняя часть этого выражения используется для определения, нужно ли печатать отдельные символы или слова. Сначала рассмотрим печать отдельных символов, которая содержит важную строку:

if read -n 1 -t 0.5 tmp

Параметр -n команды read задает длину символов, которые нужно прочитать. Здесь задано значение 1, что означает, что ввод завершается сразу после того, как пользователь введет один символ, без необходимости нажимать клавишу Enter. Параметр -t задает время ожидания ввода. Если пользователь не вводит данные или ввод не завершен в течение этого времени, чтение завершается сразу. Поскольку команда read является синхронной операцией для одного и того же shell при чтении пользовательского ввода, реализация падения символов основана на этом времени ожидания (косвенно реализуя задержку падения). Здесь установлено значение 0.5 с, так как большинство пользователей могут ввести один символ за 0.5 с. После чтения пользовательского ввода используется цикл 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 используется для перехвата специальных сигналов в shell, таких как Ctrl+C, Ctrl+D, Ctrl+Z, ESC и т.д. Обычно эти специальные сигналы обрабатываются самим shell. Поскольку мы хотим, чтобы игра завершалась более плавно, мы можем перехватить второй специальный сигнал, который является Ctrl+C, чтобы реализовать некоторую настраиваемую обработку. Например, здесь, после перехвата второго сигнала, вызывается функция doexit ниже для плавного завершения программы:

function doexit() {
  draw_border
  echo -e "\033[10;30Hthis game will exit....."
  echo -e "\033[0m"
  sleep 2
  clear
  exit 1
}

Основной поток кода

Поскольку код для каждого функционального модуля уже готов, чтобы запустить программу, нам все еще нужен основной поток кода для вызова этих функций.

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

Этот код создает точку входа для игры "падающие символы". Сначала он отображает приветственный экран и приглашение начать игру, а затем ожидает ввода от пользователя о том, хочет ли он начать игру. Если пользователь выбирает начать игру, ему будет предложено выбрать режим сложности игры. Если пользователь выбирает выйти или вводит недопустимые данные, игра не запустится.

Запуск и тестирование

Далее мы можем запустить нашу shell-игру по печати:

cd ~/project
bash shell_typing_game.sh

Shell Typing Game

Резюме

Вы только что создали проект по разработке простой игры по печати в shell. Вы можете следовать этим шагам, чтобы создать собственный проект игры по печати и выбрать различные игровые режимы и уровни сложности. Этот проект предоставляет практический опыт в написании shell-скриптов и разработке игр на основе терминала. Если вы не знакомы с некоторыми командами, использованными в курсе, такими как различные выводы команды echo, перенаправление ввода-вывода с помощью exec и перехват специальных сигналов с помощью trap, вы можете больше практиковаться, чтобы привыкнуть к их использованию.

✨ Проверить решение и практиковаться✨ Проверить решение и практиковаться✨ Проверить решение и практиковаться✨ Проверить решение и практиковаться✨ Проверить решение и практиковаться✨ Проверить решение и практиковаться✨ Проверить решение и практиковаться✨ Проверить решение и практиковаться✨ Проверить решение и практиковаться✨ Проверить решение и практиковаться