diff --git a/DbTictactoe.pro b/DbTictactoe.pro new file mode 100644 index 0000000..ae43575 --- /dev/null +++ b/DbTictactoe.pro @@ -0,0 +1,20 @@ +QT += core +QT -= gui widgets + +DBLIBS += + +TARGET = tictactoe + +PROJECT_ROOT = .. + +SOURCES += main.cpp + +HEADERS += + +FORMS += + +RESOURCES += + +TRANSLATIONS += + +include($${PROJECT_ROOT}/app.pri) diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..595b089 --- /dev/null +++ b/main.cpp @@ -0,0 +1,241 @@ +#include +#include +#include +#include +#include + +enum class FieldType { EMPTY, CIRCLE, CROSS }; + +char fieldTypeLetter(const FieldType fieldType) +{ + switch(fieldType) + { + case FieldType::EMPTY: return '_'; + case FieldType::CIRCLE: return 'O'; + case FieldType::CROSS: return 'X'; + default: qFatal("unexpected field type"); + } +} + +template +QString getFieldName(const std::array, HEIGHT> &field) +{ + QString result; + + for(int y = 0; y < HEIGHT; y++) + { + const auto &row = field.at(y); + + if(!result.isEmpty()) + result.append('-'); + + for(int x = 0; x < WIDTH; x++) + result.append(fieldTypeLetter(row.at(x))); + } + + return result; +} + +template +quint64 getFieldHash(const std::array, HEIGHT> &field) +{ + quint64 result = 0; + quint64 digitValue = 1; + + for(quint32 y = 0; y < HEIGHT; y++) + for(quint32 x = 0; x < WIDTH; x++) + { + quint8 digit = (quint8)field.at(y).at(x); + + result += digit * digitValue; + + digitValue *= 3; + } + + return result; +} + +template +FieldType getFieldWinner(const std::array, SIZE> &field) +{ + for(quint32 y = 0; y < SIZE; y++) + { + const auto &row = field.at(y); + auto last = row.at(0); + + if(last != FieldType::EMPTY) + { + auto failed = false; + for(quint32 x = 1; x < SIZE; x++) + { + if(row.at(x) != last) + { + failed = true; + break; + } + last = row.at(x); + } + + if(!failed) + return last; + } + } + + for(quint32 x = 0; x < SIZE; x++) + { + auto last = field.at(0).at(SIZE-1); + + if(last != FieldType::EMPTY) + { + auto failed = false; + for(quint32 y = 1; y < SIZE; y++) + { + if(field.at(y).at(x) != last) + { + failed = true; + break; + } + last = field.at(y).at(x); + } + + if(!failed) + return last; + } + } + + { + auto last = field.at(0).at(0); + + if(last != FieldType::EMPTY) + { + auto failed = false; + for(quint32 i = 1; i < SIZE; i++) + { + if(field.at(i).at(i) != last) + { + failed = true; + break; + } + last = field.at(i).at(i); + } + + if(!failed) + return last; + } + } + + { + auto last = field.at(0).at(SIZE-1); + + if(last != FieldType::EMPTY) + { + auto failed = false; + for(quint32 i = 1; i < SIZE; i++) + { + if(field.at(i).at(SIZE - i) != last) + { + failed = true; + break; + } + last = field.at(i).at(SIZE - i); + } + + if(!failed) + return last; + } + } + + return FieldType::EMPTY; +} + +template +QString writeField(const QDir &dir, const std::array, HEIGHT> &field) +{ + const auto fieldHash = getFieldHash(field); + const auto name = (fieldHash == 0 ? QStringLiteral("index") : QString::number(fieldHash)) + ".html"; + + qDebug() << "writing" << name; + + QFile file(dir.absoluteFilePath(name)); + + if(file.exists()) + return name; + + auto winner = getFieldWinner(field); + + const auto fieldPlayer = std::count(std::begin(field.at(0)), std::end(field.at(2)), FieldType::CIRCLE) < + std::count(std::begin(field.at(0)), std::end(field.at(2)), FieldType::CROSS) ? + FieldType::CIRCLE : FieldType::CROSS; + + if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) + qFatal("Could not open file"); + + QTextStream textStream(&file); + textStream << "" << endl; + + for(quint32 y = 0; y < HEIGHT; y++) + { + const auto &row = field.at(y); + + textStream << "" << endl; + + for(quint32 x = 0; x < WIDTH; x++) + { + textStream << "" << endl; + } + + textStream << "" << endl; + } + + textStream << "
"; + + if(row.at(x) == FieldType::EMPTY && winner == FieldType::EMPTY) + { + auto copy = field; + copy[y][x] = fieldPlayer; + const auto otherName = writeField(dir, copy); + textStream << "_"; + } + else + textStream << fieldTypeLetter(row.at(x)); + + textStream << "
" << endl; + + switch(winner) + { + case FieldType::CIRCLE: + textStream << "CIRCLE won" << endl; + break; + case FieldType::CROSS: + textStream << "CROSS won" << endl; + break; + default: + switch(fieldPlayer) + { + case FieldType::CIRCLE: + textStream << "CIRCLE plays" << endl; + break; + case FieldType::CROSS: + textStream << "CROSS plays" << endl; + break; + default: + qt_noop(); + } + } + + return name; +} + +int main(int argc, char *argv[]) +{ + QCoreApplication a(argc, argv); + + QDir dir("tictactoe"); + dir.removeRecursively(); + dir.mkpath(dir.absolutePath()); + + std::array, 3> test {}; + writeField(dir, test); + + return 0; +}