Готовое_КР_Быки_и_коровы
.pdf
3 РЕАЛИЗАЦИЯ
3.1 Игровой движок
Класс GameEngine реализует основную игровую логику, наследуясь от
QObject для интеграции с QML-интерфейсом. При создании экземпляра инициализируется генератор случайных чисел на основе текущего времени, что обеспечивает уникальные последовательности в каждой игре.
Листинг 3.1 – Конструктор GameEngine
GameEngine::GameEngine(QObject *parent) : QObject(parent)
{
qsrand(QDateTime::currentMSecsSinceEpoch());
}
Генерация секретного 4-значного числа с уникальными цифрами выполняется методом generateSecret(), где применяется алгоритм Фишера-Йетса для случайного перемешивания цифр 0-9 с последующим выбором первых четырех элементов. Это гарантирует абсолютную случайность и уникальность каждой цифры.
Листинг 3.2 – Генерация случайного 4-хзначного числа
QVector<int> GameEngine::generateSecret()
{
QVector<int> digits;
for(int i = 0; i < 10; ++i) digits.append(i);
// Алгоритм Фишера-Йетса for(int i = 0; i < 4; ++i) {
int j = i + qrand() % (10 - i); qSwap(digits[i], digits[j]);
}
return digits.mid(0, 4);
}
Управление вводом пользователя осуществляется через методы addDigit() и removeDigit(), которые динамически обновляют текущую догадку с соблюдением условий уникальности цифр. Валидация ввода реализована через регулярное выражение ^(?!.*(\d).*\1)\d{4}$, которое проверяет наличие ровно четырех
13
уникальных цифр. При попытке ввода повторяющихся символов или нецифровых
знаков система автоматически блокирует некорректные операции.
Листинг 3.3 – Методы добавления и удаления чисел
void GameEngine::addDigit(const QString &digit)
{
if(m_currentGuess.length() < 4 && !m_currentGuess.contains(digit)) { setCurrentGuess(m_currentGuess + digit);
}
}
void GameEngine::removeDigit()
{
if(!m_currentGuess.isEmpty()) { setCurrentGuess(m_currentGuess.left(m_currentGuess.length()-1));
}
}
Функция processGuess() реализует ключевой игровой механизм проверки догадки. При вызове она сначала преобразует текущую догадку из строки в вектор целочисленных значений. Создается рабочая копия секретного числа для предотвращения модификации исходных данных. В первом цикле осуществляется подсчет быков: если цифра в догадке полностью совпадает с цифрой в задуманном числе по значению и позиции, счетчик быков увеличивается, а обработанные позиции маркируются специальными значениями (-1 и -2 соответственно) для исключения повторного учета. Во втором цикле выполняется поиск коров:
проверяется наличие цифр из догадки (кроме уже помеченных как быки) в
оставшихся непомеченных позициях секрета. После подсчета формируется результат в формате строки, который добавляется в историю попыток с учетом текущего режима хранения данных. Завершается обработка сбросом текущей догадки и отправкой сигналов обновления интерфейса. При обнаружении четырех быков генерируется сигнал победы, активирующий соответствующий диалог.
Листинг 3.4 – Метод подсчета «быков» и «коров»
void GameEngine::processGuess()
{
QVector<int> guessDigits;
for(const QChar &c : m_currentGuess) guessDigits << c.digitValue();
int bulls = 0, cows = 0; QVector<int> secretCopy = m_secret;
14
Продолжение Листинга 3.4
// Подсчет быков
for(int i = 0; i < 4; ++i) { if(guessDigits[i] == secretCopy[i]) {
bulls++; secretCopy[i] = -1; guessDigits[i] = -2;
}
}
// Подсчет коров
for(int i = 0; i < 4; ++i) { if(secretCopy.contains(guessDigits[i])) cows++;
}
QString result = QString("%1: %2 бык. / %3 кор.").arg(m_currentGuess).arg(bulls).arg(cows);
if(m_saveHistory) { m_history.append(result);
} else {
m_history = QStringList(result);
}
m_currentGuess.clear(); emit guessChanged(); emit historyChanged();
if(bulls == 4) { emit victory();
}
}
При вызове checkGuess() происходит подсчет быков (полных совпадений цифры и позиции) и коров (цифр, присутствующих в секрете, но на других позициях). Алгоритм сначала идентифицирует быков, помечая обработанные позиции специальными значениями (-1), затем среди оставшихся цифр ищет коров.
Результат форматируется в строку вида "1234: 2 бык. / 1 кор." и добавляется в историю попыток с учетом текущего режима сохранения истории.
Листинг 3.5 – Функция вывода результата
void GameEngine::checkGuess()
{
if(!validate()) {
emit error("Некорректный ввод!\nИспользуйте 4 уникальные цифры"); return;
}
processGuess();
}
Доступ к хранимым данным обеспечивает history(), которая возвращает либо полный список попыток, либо только последний результат в зависимости от
15
текущего режима. Этот метод напрямую интегрируется с QML-интерфейсом, где
Repeater динамически создает элементы отображения. При начале новой игры через newGame() система автоматически очищает историю, сохраняя при этом выбранный режим хранения для следующей сессии.
Все функции синхронизированы через систему сигналов: historyChanged()
уведомляет интерфейс о любых изменениях данных, а saveHistoryChanged()
сообщает о смене режима хранения. Такая архитектура обеспечивает консистентность данных - при отключении режима сохранения история мгновенно очищается, а при активации начинает накапливать новые записи без ручного вмешательства.
Листинг 3.6 – Функции и сигналы для управления историей ввода
void GameEngine::clearHistory()
{
m_history.clear(); emit historyChanged();
}
QString GameEngine::currentGuess() const
{
return m_currentGuess;
}
QStringList GameEngine::history() const
{
return m_history;
}
QStringList GameEngine::usedDigits() const
{
return m_currentGuess.split("", QString::SkipEmptyParts);
}
bool GameEngine::saveHistory() const
{
return m_saveHistory;
}
void GameEngine::setSaveHistory(bool value)
{
if(m_saveHistory != value) { m_saveHistory = value; if(!m_saveHistory) m_history.clear(); emit saveHistoryChanged();
}
}
Код модулей разработанной программы представлен в Приложении А.
16
3.2 Интерфейс и игровая составляющая
3.2.1 Приветственный диалог
При первом запуске приложения пользователю отображается стартовый диалог с приветствием и предложением выбрать, сохранять ли историю попыток между играми. Диалог выполнен в морской цветовой гамме с градиентным фоном от цвета морской волны к темно-зеленому. Если пользователь активирует переключатель, система запомнит этот выбор для следующей игры.
Приветственный диалог также адаптирован под выбранную пользователем цветовую тему и меняет в зависимости от этого цвет текста (Рисунок 3.1).
Рисунок 3.1 – Приветственный диалог в разном представлении
После нажатия кнопки "Начать игру" инициализируется новый игровой сеанс
–происходит генерация случайного 4-значного числа с уникальными цифрами,
очищаются предыдущие данные (даже если их нет), происходит переходит к основному игровому экрану (Рисунок 3.2).
17
Рисунок 3.2 – Запуск игры с сохранением истории ввода
3.2.2 Основной экран
Интерфейс основного экрана включает заголовок "Быки и Коровы", поле для ввода догадки с подписью "Ваша догадка", цифровую клавиатуру и кнопку проверки. Пользователь может вводить цифры двумя способами: непосредственно через текстовое поле (с автоматической валидацией на уникальность цифр) или с помощью экранной клавиатуры, где уже использованные цифры автоматически блокируются. При попытке ввести повторяющуюся цифру или превысить лимит символов система игнорирует некорректный ввод. Для удаления введенных цифр предусмотрена специальная кнопка.
История попыток отображается в основной области экрана и может работать в двух режимах: если пользователь включил сохранение истории, отображаются все предыдущие попытки; в противном случае показывается только последняя догадка.
Каждая запись истории размещается на полупрозрачной темно-зеленой плашке с закругленными углами для улучшения читаемости (Рисунок 3.3).
18
Рисунок 3.3 – Ввод и проверка значений с сохранением истории и без
3.2.3 Диалог об окончании игры
При вводе загаданного значения (например, 6507, рисунок 3.3) и его проверке,
срабатывает механизм победы. Открывается диалоговое окно "ПОБЕДА!" с
предложением выбрать, сохранять ли историю для следующей игры. Независимо от выбора пользователя (кнопки "Да" или "Нет"), после закрытия диалога автоматически начинается новая игра с учетом выбранных настроек сохранения истории (Рисунок 3.4).
19
Рисунок 3.4 – Диалог завершения игры
Визуальное оформление приложения выдержано в морской тематике с использованием градиентных фонов и прозрачных элементов. Цветовая схема адаптирована как для светлых, так и для темных тем устройства – основной текст отображается белым или черным цветом в зависимости от контекста. Интерфейс адаптивен и корректно отображается, автоматически масштабируя элементы под размеры экрана.
3.3 Тестирование приложения
В ходе тестирования приложения были использованы различные варианты ввода, как при помощи экранных кнопок (Рисунок 3.5), так и при помощи клавиатуры устройства, появляющейся при нажатии на текстовое поле (Рисунок
20
3.6). Также была протестирована работоспособность кнопки удаления введенных значений.
Рисунок 3.5 – Ввод и удаление при помощи кнопок
Рисунок 3.6 – Ввод при помощи клавиатуры
Кроме того, были протестированы диалоговые окна и страницы на соответствие адаптации под выбранную цветовую тему (Рисунок 3.7).
21
Рисунок 3.7 – Экраны приложения для разных цветовых тем
Таким образом тестирование показало соответствие функциональности
приложения заявленным требованиям.
22
