Создание игры HANGMAN с Perl и SQLite для запуска в командной строке

Этот проект на высоком уровне представляет собой трехэтапный процесс, который я разделю на более мелкие подэтапы.

1. Получите список слов.

Мы могли бы использовать API или какой-нибудь другой источник. Я собираюсь использовать онлайн-генератор слов, скопировать и вставить список как файл .txt в свой текстовый редактор.
В итоге у меня должен получиться простой список, разделенный запятыми, который выглядит следующим образом:
верблюд, волосы, компьютер, абрикос, нерф, галоп …… ..и т. д. и т. д.

2. Добавьте слова в таблицу SQLite.

Я буду использовать два отдельных скрипта. Один для добавления всех моих слов в таблицу SQL и второй сценарий, который запускает игру, поскольку мне не нужно запускать код, который бы добавлял слова в базу данных каждый раз, когда я играю в игру.
Вместо просто список слов, у меня будет таблица со столбцами. Эти столбцы будут включать:
Слово - ТЕКСТ (пример: «abacus»)
Оставшиеся предположения - Целое число (пример: 4)
Количество правильных предположений - Целое число (пример: 1)
Количество неправильных догадок - INTEGER (пример: 4)
Угаданные буквы - ТЕКСТ (пример: agxer)
Правильно угаданные буквы - ТЕКСТ (пример: a)
Неправильно угаданные буквы - ТЕКСТ (пример : gxer)
Оставшиеся буквы - ТЕКСТ (пример: _bcd_f_hijklmnopqrstuvw_yz)
Угаданное слово - ТЕКСТ (пример: _b_cus)

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

3. Создайте сценарий, который позволяет вводить данные пользователем и обновляет таблицу.

На этом шаге, хотя кода будет много, процесс на самом деле довольно прост:
Получить запись из базы данных
Разрешить ввод данных пользователем (угадать букву)
Обновить текущую записывать

ШАГ 1 - СПИСОК СЛОВ

Сначала я зашел на сайт, который генерирует случайные слова, похожие на https://randomwordgenerator.com/. Вы можете подключиться к API или к любому другому, чтобы получить это. В итоге у меня получился текстовый файл из 1000 слов в следующем формате:

loot, import, boom, relief, stain, audience, heroin, conductor, frozen, reflection, constraint, mug, origin, wolf, fault, bury, ambiguous, weakness, soar, lion, mole, impress, bike, graduate…..

Поскольку каждая из этих переменных будет представлять собой отдельный элемент, мне не нужно создавать дополнительные таблицы для некоторых из этих столбцов. Технически такие вещи, как оставшиеся буквы, угаданные буквы и т. Д., Могут быть массивами и сохранены как (a, b, c, d, e….). Однако вместо этого для каждого из этих типов данных я сохраняю их как строку и объединяю результаты. Например, если до сих пор угадывались буквы (a, b, c, d), я бы сохранил это как строку «abcd». Затем, если следующая предполагаемая буква - «е», я бы связал ее с текущей строкой, используя синтаксис типа $ string1. $ строка2. Причина в том, чтобы избежать большого количества кода и необходимости использовать циклы.

Затем мне нужно будет подключиться к базе данных SQL:

my $dsn = “DBI:SQLite:hangman.sqlite”;
my %attr = (PrintError=>0, RaiseError=>1);
# connect to the database
my $dbh = DBI->connect($dsn, \%attr);
Finally, I’ll set up my table:
$dbh->do(‘PRAGMA foreign_keys = ON’);
$dbh->do(‘PRAGMA foreign_keys’);
my @ddl = (
‘CREATE TABLE GUESSEDWORDS (
id INTEGER UNIQUE NOT NULL,
WORD TEXT,
REMAINING_GUESSES INTEGER,
CORRECT INTEGER,
INCORRECT INTEGER,
GUESSED_LETTERS TEXT,
RIGHT_LETTERS TEXT,
WRONG_LETTERS TEXT,
REMAINING_LETTERS TEXT,
GUESSED_WORD TEXT
PRIMARY KEY(id)
)’,
);
for my $sql (@ddl) {
$dbh->do($sql);
}

Затем мне нужно будет соединиться со своими словами. У меня мой хранится в текстовом файле, который я открываю с помощью «open», а затем прокручиваю:

my $filename = ‘words.txt’;
# connect to and open the json file
my $json_text = do {
open(my $json_fh, “<:encoding(UTF-8)”, $filename)
or die(“Can’t open \$filename\”: $!\n”);
local $/;
<$json_fh>
};

Теперь весь список слов хранится в одной строке в переменной $ json_text. Поэтому, естественно, мне нужно, чтобы это было в массиве, чтобы я мог перебирать его и добавлять данные к каждому из них. Я сделаю это с помощью метода разделения Perl.

my @words = split “, “, $json_text;

Предыдущие данные сохраняются в массиве с названием @words. Большинство данных для каждого слова будут одинаковыми. Например, каждое слово будет начинаться с 10 предположений, но текущие предположения, а также правильные или неправильные предположения будут равны 0. Все угаданные буквы будут такими же, и будет пустая строка. Единственные две части данных, которые будут отличаться, - это само слово и угаданные буквы, которые будут просто пробелами, соответствующими длине каждого слова. Таким образом, если текущее слово - МЫШЬ, поле предполагаемых букв должно быть _ _ _ _ _. Поэтому я перебираю каждое слово в цикле и конвертирую каждое слово в серию пробелов, но вставляю и слово, и пробелы в текущую строку.

foreach my $xr (@words) {
my $length = length $xr;
my @array = split //, $xr;
my @array2 = ();
foreach my $x (@array) {
push @array2, “_”
};
my $str = join ‘ ‘, @array2;

Теперь мое исходное слово хранится в переменной $ xr, а пробелы - в переменной $ str. По-прежнему в цикле foreach я создам свой запрос и вставляю инструкцию:

my $query = “insert into CURRENTWORD (WORD, REMAINING_GUESSES, CORRECT, INCORRECT, GUESSED_LETTERS, RIGHT_LETTERS, WRONG_LETTERS, REMAINING_LETTERS, GUESSED_WORD, ACTIVE)
values (?,?,?,?,?,?,?,?,?,?) “;
my $statement = $dbh->prepare($query);
$statement->execute($xr, 10, 0, 0, “”, “”, “”, “abcdefghijklmnopqrstuvwxyz”, $str, “no”);
};

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

ШАГ 2 - СОЗДАНИЕ ЛОГИКИ

На данный момент я уже знаю поля, которые собираюсь ввести из своей базы данных. Итак, я собираюсь объявить эти переменные в начале моего файла Perl и сбросить значения позже, как только введу данные.

#!/usr/bin/perl
use DBI;
use strict;
use warnings;
my $wordID;
my $word1;
my $remaining_guesses1;
my $correct1;
my $incorrect1;
my $guessed_letters1;
my $right_letters1;
my $wrong_letters1;
my $remaining_letters1;
my $guessed_word1;

Затем я создам приветственное сообщение, которое пользователь увидит в терминале при запуске файла:

print “Welcome to Hangman! \n”;

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

my $dsn = “DBI:SQLite:hangman.sqlite”;
my %attr = (PrintError=>0, RaiseError=>1);
# connect to the database
my $dbh = DBI->connect($dsn, \%attr);

Затем я собираюсь запросить базу данных. Я хочу возвращать только одну запись за раз, так как пользователю нужно будет угадать только одно слово за раз. Я думал о разных способах сделать это, например, «если пользователь частично угадал слово, используйте эту запись». «Если нет, то используйте любую другую запись». Кажется, что SQLite предоставляет способ сделать это с помощью операторов case. Однако, потратив немного времени на изучение этого вопроса, это показалось мне более сложным, чем я думал изначально. Поэтому вместо этого я выбрал более простую альтернативу. Я просто буду каждый раз сортировать результаты и извлекать первый результат. Если у пользователя заканчиваются догадки или угадывание слова, я просто удаляю эту запись из базы данных, так что в следующий раз она фактически перейдет к следующей записи. Вот как я задам этот запрос:

my $query2 = “SELECT * FROM CURRENTWORD
ORDER BY REMAINING_GUESSES ASC
LIMIT 1”;
my $statement2 = $dbh->prepare($query2);
$statement2->execute();
while (my @data = $statement2->fetchrow_array()) {
$wordID = $data[0];
$word1 = $data[1];
$remaining_guesses1 = $data[2];
$correct1 = $data[3];
$incorrect1 = $data[4];
$guessed_letters1 = $data[5];
$right_letters1 = $data[6];
$wrong_letters1 = $data[7];
$remaining_letters1 = $data[8];
$guessed_word1 = $data[9];
};

Как видите, я сортирую список в порядке возрастания «ASC» и использую «LIMIT 1», чтобы возвращалась только одна запись. Возвращенные данные будут в массиве с первым идентификатором в качестве первичного ключа строки. Поскольку мы уже настроили таблицу, мы знаем, в каком порядке будут располагаться остальные поля, поэтому теперь я сбрасываю значения всех переменных, чтобы отразить данные, которые мы извлекли из таблицы.
Теперь я Я просто собираюсь использовать эти переменные для вывода пользователю некоторых данных о текущем слове из таблицы:

print “You have this many remaining guesses for this word: $remaining_guesses1 \n”;
print “number of correct guesses so far: $correct1 \n”;
print “number of incorrect guesses so far: $incorrect1 \n”;
print “these are the letters you have guessed: $guessed_letters1 \n”;
print “these are the letters you have guessed correctly: $right_letters1 \n”;
print “these are the letters you have guessed incorrectly: $wrong_letters1 \n”;
print “these are the remaining letters: $remaining_letters1 \n”;
print “This is your current word: $guessed_word1 \n”;

Идеально. Теперь мне нужно предоставить способ разрешить ввод данных пользователем, который можно использовать с STDIN. STDIN и STDOUT - стандартный способ использования ввода и вывода в Perl. В этом случае мы будем использовать ‹STDIN›, который поместит курсор на экран для пользователя, и после ввода символа и нажатия Enter Perl захватит все, что было введено как ввод, и мы можем сохранить его как Переменная. Вот как мы это сделаем:

print “Please Pick a Letter: “;
my $letter = <STDIN>;
chomp $letter;

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

1. Сделал ли пользователь предположение ?:

$remaining_guesses1 = $remaining_guesses1–1;

Поскольку на этом этапе кода мы знаем, что пользователь отправил ввод, а наши текущие оставшиеся предположения находятся в диапазоне от 1 до 10, мы просто вычтем это значение, сохраним как переменную и обновим базу данных, добавив новую переменную.

2. Было ли это верным предположением ?:

sub getCorrect {
if (index($word1, $letter) != -1) {
return 1;
} else {
return 0;
}
};
my $getCorrect = $correct1 + getCorrect;

Здесь я определяю подпрограмму, чтобы определить, сделал ли пользователь правильное предположение. Если в текущем слове есть индекс буквы, то возвращается 1 (истина) или 0 (ложь), если индекса нет. И я сохраняю этот результат как переменную.

3. Было ли это неправильное предположение ?:

my $getCorrect = $correct1 + getCorrect;
sub getIncorrect {
if (index($word1, $letter) != -1) {
return 0;
} else {
return 1;
}
};
my $getIncorrect = $incorrect1 + getIncorrect;

Это то же самое, что и предыдущий шаг, и, вероятно, не нужно, но я все равно включаю его по причинам.

4. Какое общее количество угаданных букв на данный момент ?:

my $alphabet = $remaining_letters1;
sub newGuessedLetters {
$alphabet =~ s/$letter/_/;
if ($guessed_letters1 eq “”){
return $letter;
} else {
return $guessed_letters1 . $letter;
}
}
my $newGuessed = newGuessedLetters;

Здесь я использую переменную $ алфавит в качестве оставшихся букв. Затем я использую регулярное выражение, чтобы заменить угаданную букву $ на символ «_». Затем я запускаю функцию и сохраняю оператор return как переменную для обновления базы данных. Эта переменная будет содержать одну или несколько букв.

5. Добавьте текущую угадываемую букву к правильным или ошибочным буквам:

sub getRightLetters {
if (getCorrect == 1) {
if ($right_letters1 eq “”){
$right_letters1 = $letter;
}
else {
$right_letters1 = $right_letters1 . $letter;
}
}elsif(getCorrect == 0) {
if ($wrong_letters1 eq “”){
$wrong_letters1 = $letter;
} else {
$wrong_letters1 = $wrong_letters1 . $letter;
}
}
};
getRightLetters;

Здесь я просто вижу результат функции getCorrect и с этой информацией обновляю либо переменную $ right_letters1, либо переменную $ error_letters1, в зависимости от результата.

6. Какое слово сейчас угадывается?

sub returnBlanks {
my $string = $word1;
my $blanks = $guessed_word1;
my @blanks = split ‘ ‘, $blanks;
my @string = split ‘’, $string;
my $length = @string;
for (my $i=0; $i < $length; $i++) {
if ($string[$i] eq $letter){
splice @blanks, $i, 1, $letter;
}
}
my $scal = join(“ “, @blanks);
return $scal;
}
my $returnedString = returnBlanks;

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

7. Теперь давайте обновим базу данных новыми данными, которые мы собрали:

$dbh->do(“UPDATE CURRENTWORD SET REMAINING_GUESSES = $remaining_guesses1 WHERE id = $wordID”);
$dbh->do(“UPDATE CURRENTWORD SET CORRECT = $getCorrect WHERE id = $wordID”);
$dbh->do(“UPDATE CURRENTWORD SET INCORRECT = $getIncorrect WHERE id = $wordID”);
$dbh->do(“UPDATE CURRENTWORD SET GUESSED_LETTERS = ‘$newGuessed’ WHERE id = $wordID”);
$dbh->do(“UPDATE CURRENTWORD SET RIGHT_LETTERS = ‘$right_letters1’ WHERE id = $wordID”);
$dbh->do(“UPDATE CURRENTWORD SET WRONG_LETTERS = ‘$wrong_letters1’ WHERE id = $wordID”);
$dbh->do(“UPDATE CURRENTWORD SET REMAINING_LETTERS = ‘$alphabet’ WHERE id = $wordID”);
$dbh->do(“UPDATE CURRENTWORD SET GUESSED_WORD = ‘$returnedString’ WHERE id = $wordID”);
sub deleteifComplete {
if (index($returnedString, “_”) == -1 || $remaining_guesses1 <= 0){
$dbh->do(“DELETE FROM CURRENTWORD WHERE id = $wordID”);
}
}
deleteifComplete;

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

Я уверен, что это может показаться немного сложным, но если у вас есть какие-либо отзывы, дайте мне знать. Спасибо!

✉️ Подпишитесь на рассылку еженедельно Email Blast от CodeBurst 🐦 Подпишитесь на CodeBurst на Twitter , просмотрите 🗺️ Дорожная карта веб-разработчиков на 2018 год и 🕸️ Изучите веб-разработку с полным стеком .