8. неделя

Problem Set 3: Hangman & Morse

Цели

  1. Написать собственные функции согласно спецификации.
  2. Работа с одноразмерным массивом и строками
  3. Научиться использовать массив в качестве параметра в функциях

Hangman

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

Изображение социального концепта игры виселица, тем не менее, её имитация не рекомендуется.
Рис. 1: Изображение социального концепта игры виселица, тем не менее, её имитация не рекомендуется.

В рамках нашей реализации первым игроком будет пользователь, который будет угадывать слово так, что всегда напишет на экран одну букву. На отгадку слова у пользователя будет всего 8 попыток. Вторым игроком будет всегда компьютер, который сначала выберет для игры случайное слово из словаря. Выбирание слово будет осуществляться в функции int get_word(char secret[]), которая будет возвращать слово длиной максимальной в 15 букв.

Вашей задачей будет написать 4 функции:

  • int is_word_guessed(const char secret[], const char letters_guessed[]) - с помощью данной функции можно будет узнать, угадано ли слово (незаглавные буквы).
  • void get_guessed_word(const char secret[], const char letters_guessed[], char guessed_word[]) - обновит выписываемое слово так, что на неотгаданных позициях будет находиться символ '_', а на уже отгаданных - конкретные буквы.
  • void get_available_letters(const char letters_guessed[], char available_letters[]) - обновит список букв, которые ещё не были использованы для отгадавания.
  • void hangman(const char secret[]) - содержит в себе функционал самой игры.

Все 5 функций (4, которые вам предстоит написать, и функция get_word(), которая уже готова) будут находиться в файле hangman.c. Их декларации находятся в файле hangman.h. Сама программа будет находиться в файле main.c, которая считает случайное слово из словаря с помощью функции get_word() и отдаст на вход функции hangman().

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

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

Не забудьте игроку напомнить, сколько попыток у него осталось, чтобы отгадать слово. Попытка будет отсчитываться только в случае, если игрок ввёл букву, которая в слове не находится. В случае, если пользователь будет пытаться ввести снова ввести уже использованную букву, попытки он не потеряет, а просто будет оповещён о том, что буква уже использовалась ранее.

Игра заканчивается, если пользователь отгадал слово или у него закончились попытки, в последнем случае, неотгаданное слово будет выведено на экран.

Task 1: Is the Word Guessed?

Напишите функцию int is_word_guessed(const char secret[], const char letters_guessed[]) с двумя параметрами:

  • const char secret[] - Строка, представляющая заданное слово (малыми буквами)
  • const char letters_guessed[] - Строка, содержащая все использовавшиеся буквы на данный момент

Функция возвращает 1, если секретное слово из параметра secret было отгадано (его можно составить из букв, сохранённых в параметре letters_guessed). В противном случае функция возвращает 0.

Пример использования функции:

printf("%d\n", is_word_guessed("secret", "aeiou"));
// prints: 0
printf("%d\n", is_word_guessed("hello", "aeihoul"));
// prints: 1

Комментарий

Во всей программе действует правило, согласно которому, используются только слова, написанные малыми буквами.

Task 2: Update of the Guessed Word

Напишите функцию void get_guessed_word(const char secret[], const char letters_guessed[], char guessed_word[]) с тремя параметрами:

  • const char secret[] - Строка, содержащая загаданное слово (малыми буквами)
  • const char letters_guessed[] - Строка, содержащая все на данный момент отгаданными буквами
  • char guessed_word[] - параметр на выходе, представляющий отгадываемое слово

Функция не возвращает ничего (void)

Функция обновляет guessed_word так, что на неотгаданных позициях будет '_', а на отгаданных - открытые соответствующие буквы.

Пример использования функции:

char result[30];
get_guessed_word("container", "arpstxgoieyu", result);
// result = "_o_tai_er"

Комментарий

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

Task 3: Update of the Available Letters

Напишите функцию void get_available_letters(const char letters_guessed[], char available_letters[]) с двумя параметрами:

  • char letters_guessed[] - Строка, содержащая все на данный момент отгаданными буквами (малыми буквами)
  • char available_letters[] - Строка, содержащая буквы алфавита за исключением уже использованных слов.

Функция не возвращает ничего (void)

Функция обновляет строку доступных букв available_letters путем удаления из неё букв из letters_guessed. Список букв внутри будет отсортирован по возрастанию.

Пример использования функции:

char result[30];
get_available_letters("arpstxgoieyu", result);
// result = "bcdfhjklmnqvwz"

Комментарий

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

Task 4: Game

Напишите функцию void hangman(const char secret[]) с одним параметром:

  • const char secret[] - Строка, содержащая загаданное слово

Функция не возвращает ничего (void)

Функция реализует интерактивную игру между пользователем и компьютером с использованием вами написанных функций int is_word_guessed(), void get_guessed_word() и void get_available_letters().

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

При этом игрок сможет вводить с клавиатуры любые буквы (как малые так и заглавные) данный функционал реализуйте прямо в функции hangman().

Не забудьте игроку напомнить, сколько попыток у него осталось, чтобы отгадать слово. Попытка будет отсчитываться только в случае, если игрок ввёл букву, которая в слове не находится. В случае, если пользователь будет пытаться ввести снова ввести уже использованную букву, попытки он не потеряет, а просто будет оповещён о том, что буква уже использовалась ранее.

Слово, полученное из функции get_guessed_word() дли улучшения читабельности выписывает в виде l a _ _ s _ a _ e.

Если игрок введёт целое слово, игра сразу заканчивается, оповестив игрока, была ли игра выиграна или нет.

Program Examples

Если пользователь отгадает слово, игра заканчивается:

$ ./hangman
Welcome to the game, Hangman!
I am thinking of a word that is 7 letters long.
-------------
You have 8 guesses left.
Available letters: abcdefghijklmnopqrstuvwxyz
Please guess a letter: l
Good guess: _ _ _ _ l _ _
-------------
You have 8 guesses left.
Available letters: abcdefghijkmnopqrstuvwxyz
Please guess a letter: e
Good guess: _ _ _ _ l e _
-------------
You have 8 guesses left.
Available letters: abcdfghijkmnopqrstuvwxyz
Please guess a letter: d
Oops! That letter is not in my word: _ _ _ _ l e _
-------------
You have 7 guesses left.
Available letters: abcfghijkmnopqrstuvwxyz
Please guess a letter: a
Oops! That letter is not in my word: _ _ _ _ l e _
-------------
You have 6 guesses left.
Available letters: bcfghijkmnopqrstuvwxyz
Please guess a letter: U
Good guess: _ u _ _ l e _
-------------
You have 6 guesses left.
Available letters: bcfghijkmnopqrstvwxyz
Please guess a letter: u
Oops! You've already guessed that letter: _ u _ _ l e _
-------------
You have 6 guesses left.
Available letters: bcfghijkmnopqrstvwxyz
Please guess a letter: s
Good guess: _ u _ _ l e s
-------------
You have 6 guesses left.
Available letters: bcfghijkmnopqrtvwxyz
Please guess a letter: p
Good guess: p u _ p l e s
-------------
You have 6 guesses left.
Available letters: bcfghijkmnoqrtvwxyz
Please guess a letter: r
Good guess: p u r p l e s
-------------
Congratulations, you won!

Если игрок исчерпает все 8 попыток, игра заканчивается, а загаданное слово выписывается на экран:

$ ./hangman
Welcome to the game, Hangman!
I am thinking of a word that is 10 letters long.
-------------
You have 8 guesses left.
Available letters: abcdefghijklmnopqrstuvwxyz
Please guess a letter: a
Oops! That letter is not in my word: _ _ _ _ _ _ _ _ _ _
-------------
You have 7 guesses left.
Available letters: bcdefghijklmnopqrstuvwxyz
Please guess a letter: e
Good guess: _ _ _ e _ e _ _ e _
-------------

...

-------------
You have 1 guesses left.
Available letters: hjklmnpqrstvwxyz
Please guess a letter: h
Oops! That letter is not in my word: u _ d e _ e _ _ e d
-------------
Sorry, you ran out of guesses. The word was undeserved.

В случае, если игрок вводит полностью слово, игра заканчивается и игрок оповещается сразу о том, выиграна ли игра:

$ ./hangman
Welcome to the game, Hangman!
I am thinking of a word that is 4 letters long.
-------------
You have 8 guesses left.
Available letters: abcdefghijklmnopqrstuvwxyz
Please guess a letter: e
Oops! That letter is not in my word: _ _ _ _
-------------
You have 7 guesses left.
Available letters: abcdfghijklmnopqrstuvwxyz
Please guess a letter: ball
Congratulations, you won!
$ ./hangman
Welcome to the game, Hangman!
I am thinking of a word that is 4 letters long.
-------------
You have 8 guesses left.
Available letters: abcdefghijklmnopqrstuvwxyz
Please guess a letter: o
Good guess: _ o _ _
-------------
You have 8 guesses left.
Available letters: abcdefghijklmnpqrstuvwxyz
Please guess a letter: word
Sorry, bad guess. The word was goal.

Если игрок введёт символ, не являющийся буквой, количество попыток не уменьшается:

$ ./hangman
Welcome to the game, Hangman!
I am thinking of a word that is 7 letters long.
-------------
You have 8 guesses left.
Available letters: abcdefghijklmnopqrstuvwxyz
Please guess a letter: a
Good guess: _ a _ _ _ a _
-------------
You have 8 guesses left.
Available letters: bcdefghijklmnopqrstuvwxyz
Please guess a letter: e
Oops! That letter is not in my word: _ a _ _ _ a _
-------------
You have 7 guesses left.
Available letters: bcdfghijklmnopqrstuvwxyz
Please guess a letter: @
Oops! '@' is not a valid letter: _ a _ _ _ a _
-------------
You have 7 guesses left.
Available letters: bcdfghijklmnopqrstuvwxyz
Please guess a letter: h
Good guess: h a _ _ _ a _
-------------
You have 7 guesses left.
Available letters: bcdfgijklmnopqrstuvwxyz
Please guess a letter: N
Good guess: h a n _ _ a n
-------------
You have 7 guesses left.
Available letters: bcdfgijklmopqrstuvwxyz
Please guess a letter: hangman
Congratulations, you won!

Morse Code

Азбука Морзе - это телекоммуникационная технология, которая кодирует символы в последовательность сигналов двух видов - длинных и коротких. Именно с помощью этой технологии "Титаник" вызвал помощь при столкновении с айсбергом. Вторым вашим заданием будет как раз реализация инструмента, способного кодировать текст в азбуку Морзе и наоборот. Его таблицу кодировки можно найти, например здесь.

В нашем задании коротким сигналом будет символ '.', а длинным - символ '-'. Помните, что кодируемые символы в азбуке Морзе должны быть разделены пробелом.

Task 5:

Напишите функцию void text_to_morse(const char text[], char output[]) с двумя параметрами:

  • const char text[] - массив символов (строка) в виде текста, который будет переведен функцией в код Морзе,
  • char output[] - выходной массив преобразованный в символы кода Морзе.

Функция не возвращает никакого значения. Функция заполняет массив символами азбуки Морзе таким образом, чтобы каждый закодированный символ был разделен пробелом.

Пример использования:

char output[150];

text_to_morse("Hello", output);
puts(output);
//prints: .... . .-.. .-.. ---

Task 6:

Напишите функцию void morse_to_text(const char morse[], char output[]) с двумя параметрами:

  • const char morse[] - массив символов азбуки Морзе, которые функция должна перевести обратно в алфавит,
  • char output[] - массив вывода преобразованной строки в символы алфавита.

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

Пример использования:

char output[150];

morse_to_text(".... . .-.. .-.. ---", output);
//prints: HELLO

Task 7:

Напишите функциюu int is_morse_code_valid(const char morse[]) с одним параметрам:

  • const char morse[] - массив знаков азбуки Морзе.

Функция возвращает 1, если код Морзе валиден, то есть каждый найденный символ может быть идентифицирован и переведен. Функция возвращает 0, если найден хотя бы один невалидный символ кода Морзе.

Пример использования:

if (is_morse_code_valid(".... . .-.. .-.. ---")) {
     printf("Code is valid! \n");
   } else {
    printf("Code is invalid! \n");
   }
    //prints: Code is valid!

   //....
    if (is_morse_code_valid(".... . .-.--. .-.. ---")) {
     printf("Code is valid! \n");
   } else {
    printf("Code is invalid! \n");
   }

   //prints: Code is invalid!

Project Submission

Задание сдайте до 23.11.2023 (четверг). Подробный способ сдачи уточните у вашего педагога.

Задание сдаётся с помощью системы контроля версий Git на сервере git.kpi.fei.tuke.sk.

Название вашего проекта должно быть точно в формате: zap-2023-id.

Сохраните иерархию файлов и директорий:

.
├── ps3
│   ├── hangman.c
│   ├── hangman.h
│   ├── morse.h
│   ├── morse.c
│   ├── main.c
└── README

Значение отдельных файлов:

  • README - файл, в котором указывается группа, которую вы посещаете на практиках, должна быть строго в формате:
GROUP : C1
  • /ps3/hangman.c - Программный код библиотеки для игры hangman.
  • /ps3/hangman.h - Заголовочный файл библиотеки для игры hangman.
  • /ps3/morse.h - Заголовочный файл библиотеки morse.
  • /ps3/morse.c - Zdrojový kód pre morse.
  • /ps3/main.c - Программный код, содержащий главную функцию main().

Предупреждение

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

Предупреждение

Названия файлов README чувствительны к регистру в названии

Комментарий

В случае, если в проекте будут найдены другие файлы, ошибкой это считаться не будет.

Предупреждение

В случае, если вы используете Makefile, сборку проекта осуществляйте с помощью make all.

Project Skeleton

По следующей ссылке скачайте архив hangman.zip, содержащий скелет проекта. Данный архив содержит следующие файлы:

  • words.txt - Файл, содержащий 55900 различных английских слов.
  • hangman.h - Заголовочный файл, в котором содержатся декларации всех обязательных функций игры.
  • hangman.c - Код программы, в котором будет находиться конечная реализация игры, а также уже сделанная функция get_word() для загрузки случайного слова из words.txt.
  • Makefile - файл для сборки проекта, которую осуществите с помощью make all.
  • morse.h - Заголовочный файл, содержащий объявления всех необходимых функций морзянки.
  • morse.c - Zdrojový súbor, v ktorom sa bude nachádzať Vaša výsledná implementácia.

В среде OS Linux можете для загрузи использовать wget в виде:

wget http://kurzy.kpi.fei.tuke.sk/zap/resources/hangman.zip

Assessment and Testing

За задание можно получить max. 10 баллов. Любые 2 балла вам зачтутся в счёт зачёта, остальные как бонус на экзамене. Количество баллов будет зависеть от результатов тестов, будут проверяться:

  • Иерархия файлов (находятся ли в репозитории все нужные файлы).
  • Функционал вашей реализации.

Сборка будет осуществляться с помощью компилятора gcc со следующими параметрами:

gcc -std=c11 -Werror -Wall -lm

Если вы используете нами предложенный Makefile, сборку проекта выполните с помощью:

make all

Ошибкой будет считаться:

  • Использование глобальной переменной.
  • Если в вашей реализации будут использованы функции, отличный от 4 разрешённых.
  • Ошибки во время компиляции (предупреждения трактуются как ошибки).
  • Если ваша реализация не пройдёт каким-то из тестов.

Тестирование проектов будет проходить каждые 3 часа, а именно: 0300, 0600, 0900, 1200, 1500, 1800, 2100 и 2400.

Проект будет проверен на плагиат. Соблюдайте этический кодекс! В случае выявления того факта, что вы сдали не ваше задание, вы рискуете быть исключенным с предмета.