
Готовое_КР_Быки_и_коровы
.pdfЗАКЛЮЧЕНИЕ
В ходе выполнения курсовой работы была успешно разработана и реализована мобильная версия классической логической игры "Быки и коровы" для операционной системы "Аврора". Основным достижением проекта стало создание игрового движка на C++ с эффективными алгоритмами генерации секретных чисел с использованием алгоритма Фишера-Йетса и оптимизированного двухэтапного метода подсчёта результатов. Разработанный движок обеспечивает высокую производительность даже на устройствах с ограниченными ресурсами, что особенно важно для мобильной платформы.
Интерфейс приложения, реализованный на QML, полностью адаптирован под особенности ОС "Аврора", включая поддержку системных тем (светлой и тёмной),
жестов управления и стандартов безопасности. Особое внимание было уделено созданию интуитивно понятного пользовательского опыта: реализованы два режима ввода (экранная клавиатура и цифровая панель), гибкая система истории попыток с настройками сохранения ввода между играми, а также визуально привлекательные диалоговые окна с градиентным оформлением в морской цветовой гамме.
Ключевым преимуществом разработки стала интеграция с платформой
"Аврора" без зависимости от Android-эмуляции, что обеспечивает не только повышенную производительность, но и полное соответствие требованиям информационной безопасности российской ОС. Приложение демонстрирует потенциал платформы для создания современных, безопасных и производительных решений, соответствующих мировым стандартам.
23
СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
1. Официальная документация ОС "Аврора". Разработка нативных
приложений. Версия 4.0. // ООО "Открытая мобильная платформа", 2024. URL:
https://auroraos.ru/dev/docs/ (дата обращения: 29.05.2025).
2.Разработка приложений для ОС "Аврора" на C++/Qt / Петров А.В., Иванова С.К. // Журнал "Системы высокой доступности". – 2023. – № 4. – С. 45-58.
3.Гибридная разработка с QML и C++ / Шмидт В.Ф. СПб.: БХВ-Петербург,
2024. – 416 с. ISBN 978-5-9775-4123-6.
4.Современные методы генерации случайных чисел / Кнут Д. Искусство программирования. Том 2. – М.: Вильямс, 2019. – 832 с.
5.Официальный репозиторий примеров для Aurora SDK / GitHub, 2024. URL: https://github.com/auroraos/aurora-examples (дата обращения: 29.05.2025).
24
ПРИЛОЖЕНИЯ
Приложение А – Исходный код программы.
25

Приложение А
Листинг А.1 – MainPage.qml
import QtQuick 2.0
import Sailfish.Silica 1.0 import Nemo.Notifications 1.0 import Game 1.0
ApplicationWindow { id: appWindow
initialPage: mainPage
property bool firstRun: true
readonly property color seaWave: "#2E8B57"
Page {
id: mainPage
allowedOrientations: Orientation.All
SilicaFlickable { anchors.fill: parent
contentHeight: contentColumn.height
Rectangle { anchors.fill: parent gradient: Gradient {
GradientStop { position: 0.0; color: seaWave } GradientStop { position: 1.0; color: "#1D3C34" }
}
|
} |
|
Column { |
|
id: contentColumn |
|
width: parent.width |
|
spacing: Theme.paddingLarge |
|
visible: !appWindow.firstRun |
|
PageHeader { |
|
title: "Быки и Коровы" |
|
titleColor: Theme.primaryColor |
|
} |
|
TextField { |
|
id: manualInput |
|
width: parent.width - 2*Theme.horizontalPageMargin |
|
anchors.horizontalCenter: parent.horizontalCenter |
|
placeholderText: "Введите 4 цифры" |
|
color: Theme.primaryColor |
|
inputMethodHints: Qt.ImhDigitsOnly |
|
maximumLength: 4 |
|
validator: RegExpValidator { |
|
regExp: /^(?!.*(\d).*\1)\d{4}$/ |
|
} |
|
text: game.currentGuess |
|
label: "Ваша догадка" |
// |
placeholderColor: "#A0D3D3" |
|
onTextChanged: { |
|
if(text !== game.currentGuess) { |
26

Продолжение Листинга А.1
game.setCurrentGuess(text)
}
}
background: Rectangle { color: "transparent"
border.color: Theme.primaryColor radius: 5
}
}
Button {
text: "Проверить"
anchors.horizontalCenter: parent.horizontalCenter enabled: game.currentGuess.length === 4 onClicked: game.checkGuess()
color: seaWave highlighted: true width: parent.width/2
}
Grid {
width: parent.width - 2*Theme.horizontalPageMargin columns: 5
spacing: Theme.paddingSmall anchors.horizontalCenter: parent.horizontalCenter
Repeater { model: 10 Button {
width: parent.width/5 - Theme.paddingSmall height: width
text: index
enabled: game.usedDigits.indexOf(text) === -1 &&
game.currentGuess.length < 4
onClicked: game.addDigit(text) color: "#1D3C34"
highlighted: true Label {
text: parent.text
color: Theme.primaryColor font.pixelSize: Theme.fontSizeLarge anchors.centerIn: parent
}
}
}
Button {
width: parent.width/5 - Theme.paddingSmall height: width
text: " "
onClicked: game.removeDigit() color: "#1D3C34"
highlighted: true Label {
text: parent.text
color: Theme.primaryColor font.pixelSize: Theme.fontSizeLarge anchors.centerIn: parent
27

Продолжение Листинга А.1
}
}
}
Repeater {
model: game.saveHistory ? game.history : (game.history.length > 0 ? [game.history[game.history.length-1]] : [])
delegate: ListItem { width: parent.width
contentHeight: Theme.itemSizeSmall
Rectangle { anchors.fill: parent color: "#1D3C34" opacity: 0.8
radius: 5
}
Label {
text: modelData anchors.centerIn: parent color: Theme.primaryColor
font.pixelSize: Theme.fontSizeSmall
}
}
}
}
}
Dialog {
id: firstRunDialog anchors.fill: parent canAccept: true
visible: appWindow.firstRun
background: Rectangle { color: seaWave radius: 15
}
Column {
anchors.fill: parent spacing: Theme.paddingLarge PageHeader {
title: "Добро пожаловать!" titleColor: Theme.primaryColor
}
TextSwitch {
text: "Сохранять историю попыток?" checked: false
onCheckedChanged: game.saveHistory = checked
}
Button {
text: "Начать игру"
anchors.horizontalCenter: parent.horizontalCenter onClicked: {
appWindow.firstRun = false
28

Продолжение Листинга А.1
firstRunDialog.accept()
game.newGame()
}
color: Theme.primaryColor highlighted: true
}
}
}
// Диалог победы
Dialog {
id: victoryDialog anchors.fill: parent canAccept: true background: Rectangle {
color: seaWave radius: 15
}
Column {
width: parent.width spacing: Theme.paddingLarge
PageHeader {
title: "ПОБЕДА!"
titleColor: Theme.primaryColor
}
Text {
text: "Хотите сохранять историю для следующей игры?" width: parent.width
horizontalAlignment: Text.AlignHCenter color: Theme.primaryColor font.pixelSize: Theme.fontSizeMedium
}
Row {
width: parent.width spacing: Theme.paddingLarge
Button {
text: "Да"
width: parent.width/2 - Theme.paddingLarge onClicked: {
game.saveHistory = true victoryDialog.accept() game.newGame()
}
color: "#1D3C34" highlighted: true
}
Button {
text: "Нет"
width: parent.width/2 - Theme.paddingLarge onClicked: {
game.saveHistory = false victoryDialog.accept() game.newGame()
}
29

Продолжение Листинга А.1
color: "#1D3C34" highlighted: true
}
}
}
}
}
GameEngine { id: game
onError: notification.show(message) onVictory: victoryDialog.open()
}
Notification {
id: notification expireTimeout: 3000
}
}
Листинг А.2 – GameEngine.cpp
#include "GameEngine.h" #include <QDateTime> #include <QDebug> #include <QSet>
GameEngine::GameEngine(QObject *parent) : QObject(parent)
{
qsrand(QDateTime::currentMSecsSinceEpoch());
}
void GameEngine::newGame()
{
m_secret = generateSecret(); m_currentGuess.clear(); m_history.clear();
emit guessChanged(); emit historyChanged();
qDebug() << "New game started. Secret:" << m_secret;
}
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);
}
void GameEngine::setCurrentGuess(const QString &guess)
{
if(guess != m_currentGuess && guess.length() <= 4 &&
QRegExp("^(?!.*(\\d).*\\1)\\d*$").exactMatch(guess))
{
30

Продолжение Листинга А.2
{
m_currentGuess = guess; emit guessChanged();
}
}
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));
}
}
bool GameEngine::validate() const
{
if(m_currentGuess.length() != 4) return false;
QSet<QChar> uniqueChars;
for(const QChar &c : m_currentGuess) { uniqueChars.insert(c);
}
return uniqueChars.size() == 4;
}
void GameEngine::checkGuess()
{
if(!validate()) {
emit error("Некорректный ввод!\nИспользуйте 4 уникальные цифры"); return;
}
processGuess();
}
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;
// Подсчет быков
for(int i = 0; i < 4; ++i) { if(guessDigits[i] == secretCopy[i]) {
bulls++; secretCopy[i] = -1; guessDigits[i] = -2;
}
}
// Подсчет коров
31

Продолжение Листинга А.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();
}
}
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();
}
}
32