File indexing completed on 2022-09-27 13:19:29

0001 /*
0002     SPDX-FileCopyrightText: 2006 Matthew Williams <matt@milliams.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 //header
0008 #include "ksquareswindow.h"
0009 
0010 //qt
0011 #include <QAction>
0012 #include <QDebug>
0013 #include <QLabel>
0014 #include <QStandardItemModel>
0015 #include <QStatusBar>
0016 #include <QTimer>
0017 
0018 //kde
0019 #include <KConfigDialog>
0020 #include <KActionCollection>
0021 #include <KLocalizedString>
0022 #include <KScoreDialog>
0023 #include <KStandardGameAction>
0024 
0025 //generated
0026 #include "settings.h"
0027 
0028 //classes
0029 #include "aicontroller.h"
0030 #include "gameboardview.h"
0031 
0032 //ui
0033 #include "newgamedialog.h"
0034 #include "scoresdialog.h"
0035 
0036 KSquaresWindow::KSquaresWindow() : KXmlGuiWindow(), m_view(new GameBoardView(this)), m_scene(nullptr)
0037 {
0038     setCentralWidget(m_view);
0039     QTimer::singleShot(0, this, &KSquaresWindow::initObject);
0040 }
0041 
0042 KSquaresWindow::~KSquaresWindow()
0043 {
0044     delete m_view;
0045     delete m_scene;
0046     delete sGame;
0047 }
0048 
0049 void KSquaresWindow::initObject()
0050 {
0051     sGame = new KSquaresGame();
0052     connect(sGame, &KSquaresGame::takeTurnSig, this, &KSquaresWindow::playerTakeTurn);
0053     connect(sGame, &KSquaresGame::gameOver, this, &KSquaresWindow::gameOver);
0054     m_view->setRenderHints(QPainter::Antialiasing);
0055     m_view->setFrameStyle(QFrame::NoFrame);
0056     setupActions();
0057     m_player = new QLabel(i18n("Current Player"));
0058     statusBar()->addPermanentWidget(m_player);
0059     statusBar()->show();
0060     setAutoSaveSettings();
0061 
0062     gameNew();
0063 }
0064 
0065 void KSquaresWindow::showHighscores()
0066 {
0067     KScoreDialog ksdialog(KScoreDialog::Name, this);
0068     ksdialog.addLocalizedConfigGroupName(qMakePair(QByteArray("Easy"), i18n("Easy")));
0069     ksdialog.addLocalizedConfigGroupName(qMakePair(QByteArray("Medium"), i18n("Medium")));
0070     ksdialog.addLocalizedConfigGroupName(qMakePair(QByteArray("Hard"), i18n("Hard")));
0071     ksdialog.exec();
0072 }
0073 
0074 void KSquaresWindow::gameNew()
0075 {
0076     //load settings
0077     NewGameDialog dialog(this);
0078 
0079     //create indexed arrays of the widgets
0080     QList<QLineEdit *> nameLineEditList;
0081     nameLineEditList.append(dialog.playerOneName);
0082     nameLineEditList.append(dialog.playerTwoName);
0083     nameLineEditList.append(dialog.playerThreeName);
0084     nameLineEditList.append(dialog.playerFourName);
0085     QList<QCheckBox *> humanCheckBoxList;
0086     humanCheckBoxList.append(dialog.playerOneHuman);
0087     humanCheckBoxList.append(dialog.playerTwoHuman);
0088     humanCheckBoxList.append(dialog.playerThreeHuman);
0089     humanCheckBoxList.append(dialog.playerFourHuman);
0090 
0091     //get settings from file
0092     for (int i = 0; i < Settings::playerNames().size(); i++) {
0093         nameLineEditList.at(i)->setText(Settings::playerNames().at(i));
0094     }
0095     for (int i = 0; i < Settings::playerNames().size(); i++) {
0096         if (Settings::humanList().at(i) == 2) {
0097             humanCheckBoxList.at(i)->setCheckState(Qt::Checked);
0098         } else {
0099             humanCheckBoxList.at(i)->setCheckState(Qt::Unchecked);
0100         }
0101     }
0102     dialog.spinNumOfPlayers->setValue(Settings::numOfPlayers());
0103     dialog.spinHeight->setValue(Settings::boardHeight());
0104     dialog.spinWidth->setValue(Settings::boardWidth());
0105     if (Settings::quickStart() == 2) {
0106         dialog.quickStartCheck->setCheckState(Qt::Checked);
0107     } else {
0108         dialog.quickStartCheck->setCheckState(Qt::Unchecked);
0109     }
0110 
0111     //run dialog
0112     dialog.exec();
0113     if (dialog.result() == QDialog::Rejected) {
0114         return;
0115     }
0116 
0117     //save settings
0118     Settings::setNumOfPlayers(dialog.spinNumOfPlayers->value());
0119 
0120     QStringList tempNames;
0121     for (int i = 0; i <= 3; i++) {
0122         tempNames.append(nameLineEditList.at(i)->text());
0123     }
0124     Settings::setPlayerNames(tempNames);
0125 
0126     QList<int> tempHuman;
0127     for (int i = 0; i <= 3; i++) {
0128         tempHuman.append(humanCheckBoxList.at(i)->checkState());
0129     }
0130     Settings::setHumanList(tempHuman);
0131 
0132     Settings::setBoardHeight(dialog.spinHeight->value());
0133     Settings::setBoardWidth(dialog.spinWidth->value());
0134     Settings::setQuickStart(dialog.quickStartCheck->checkState());
0135     Settings::self()->save();
0136 
0137     gameReset();
0138 }
0139 
0140 void KSquaresWindow::gameReset()
0141 {
0142     //create players
0143     QVector<KSquaresPlayer> playerList;
0144     for (int i = 0; i < Settings::numOfPlayers(); i++) {
0145         QColor color;
0146         switch (i) {
0147         case 0: //Red
0148             color = QColor(191, 3, 3); //or darker: (156,15,15);
0149             break;
0150         case 1: //Blue
0151             color = QColor(0, 67, 138); //or darker: (0,49,110);
0152             break;
0153         case 2: //Green
0154             color = QColor(0, 137, 44); //or darker: (0,110,41);
0155             break;
0156         case 3: //Yellow
0157             color = QColor(243, 195, 0); //or darker: (227,173,0);
0158             break;
0159         default:
0160             qCritical() << "KSquaresGame::playerSquareComplete(); currentPlayerId() != 0|1|2|3";
0161         }
0162         playerList.append(KSquaresPlayer(Settings::playerNames().at(i), color, Settings::humanList().at(i)));
0163     }
0164 
0165     //create physical board
0166     GameBoardScene *temp = m_scene;
0167     m_scene = new GameBoardScene(Settings::boardWidth(), Settings::boardHeight());
0168 
0169     m_view->setScene(m_scene);
0170     delete temp;
0171 
0172     m_view->setBoardSize(); //refresh board zooming
0173 
0174     //start game etc.
0175     sGame->createGame(playerList, Settings::boardWidth(), Settings::boardHeight());
0176     connect(m_scene, &GameBoardScene::lineDrawn, sGame, &KSquaresGame::addLineToIndex);
0177     //QT5: VERIFY SIGNAL DOESN'T EXIST connect(m_scene,&GameBoardScene::signalMoveRequest,this,&KSquaresWindow::slotMoveRequest);
0178     connect(sGame, &KSquaresGame::drawLine, m_scene, &GameBoardScene::drawLine);
0179     connect(sGame, &KSquaresGame::highlightMove, m_scene, &GameBoardScene::highlightLine);
0180     connect(sGame, &KSquaresGame::drawSquare, m_scene, &GameBoardScene::drawSquare);
0181 
0182     if (Settings::quickStart() == 2) {
0183         //This is being done before sGame->start(); to avoid the players cycling
0184         aiController ai(-1, sGame->lines(), QList<int>(), sGame->boardWidth(), sGame->boardHeight());
0185         QList<int> lines = ai.autoFill(8);  //There will be 8 possible safe move for the players
0186         QListIterator<int> i(lines);
0187         while (i.hasNext()) {
0188             sGame->addLineToIndex(i.next());
0189         }
0190     }
0191     sGame->start();
0192 }
0193 
0194 void KSquaresWindow::gameOver(const QVector<KSquaresPlayer> &_playerList)
0195 {
0196     QVector<KSquaresPlayer> playerList = _playerList;
0197     std::sort(playerList.begin(), playerList.end(), std::greater<KSquaresPlayer>());
0198     //m_scene->displayScoreTable(playerList);
0199 
0200     ScoresDialog scoresDialog(this);
0201     scoresDialog.setWindowTitle(i18nc("@title:window", "Scores"));
0202 
0203     auto scoreTableModel = new QStandardItemModel(&scoresDialog);
0204     scoreTableModel->setRowCount(playerList.size());
0205     scoreTableModel->setColumnCount(2);
0206     scoreTableModel->setHeaderData(0, Qt::Horizontal, i18n("Player Name"));
0207     scoreTableModel->setHeaderData(1, Qt::Horizontal, i18n("Completed Squares"));
0208 
0209     for (int i = 0; i <  playerList.size(); i++) {
0210         scoreTableModel->setItem(i, 0, new QStandardItem(playerList.at(i).name()));
0211         scoreTableModel->item(i, 0)->setEditable(false);
0212 
0213         QString temp;
0214         temp.setNum(playerList.at(i).score());
0215         scoreTableModel->setItem(i, 1, new QStandardItem(temp));
0216         scoreTableModel->item(i, 1)->setEditable(false);
0217     }
0218 
0219     scoresDialog.scoreTable->setModel(scoreTableModel);
0220     scoresDialog.scoreTable->resizeColumnsToContents();
0221     scoresDialog.exec();
0222 
0223     if (playerList.at(0).isHuman()) {
0224         int score = (int)(static_cast<double>(playerList.at(0).score()) - (static_cast<double>(Settings::boardWidth() * Settings::boardHeight()) / static_cast<double>(playerList.size())));
0225 
0226         KScoreDialog ksdialog(KScoreDialog::Name, this);
0227         switch (Settings::difficulty()) {
0228         case 0:
0229             ksdialog.setConfigGroup(qMakePair(QByteArray("Easy"), i18n("Easy")));
0230             ksdialog.addLocalizedConfigGroupName(qMakePair(QByteArray("Medium"), i18n("Medium")));
0231             ksdialog.addLocalizedConfigGroupName(qMakePair(QByteArray("Hard"), i18n("Hard")));
0232             break;
0233         case 1:
0234             ksdialog.setConfigGroup(qMakePair(QByteArray("Medium"), i18n("Medium")));
0235             ksdialog.addLocalizedConfigGroupName(qMakePair(QByteArray("Easy"), i18n("Easy")));
0236             ksdialog.addLocalizedConfigGroupName(qMakePair(QByteArray("Hard"), i18n("Hard")));
0237             break;
0238         case 2:
0239             ksdialog.setConfigGroup(qMakePair(QByteArray("Hard"), i18n("Hard")));
0240             ksdialog.addLocalizedConfigGroupName(qMakePair(QByteArray("Easy"), i18n("Easy")));
0241             ksdialog.addLocalizedConfigGroupName(qMakePair(QByteArray("Medium"), i18n("Medium")));
0242             break;
0243         }
0244         KScoreDialog::FieldInfo scoreInfo;
0245         scoreInfo[KScoreDialog::Name] = playerList.at(0).name();
0246         scoreInfo[KScoreDialog::Score].setNum(score);
0247 
0248         if (ksdialog.addScore(scoreInfo, KScoreDialog::AskName)) {
0249             ksdialog.exec();
0250         }
0251     }
0252 }
0253 
0254 void KSquaresWindow::playerTakeTurn(KSquaresPlayer *currentPlayer)
0255 {
0256     ////qDebug() << "void KSquares::playerTakeTurn(KSquaresPlayer* currentPlayer)";
0257     m_player->setText(QStringLiteral("<font color=\"%1\">%2</font>")
0258                       .arg(currentPlayer->colour().name())
0259                       .arg(currentPlayer->name()));
0260     if (currentPlayer->isHuman()) {
0261         //Let the human player interact with the board through the GameBoardView
0262 
0263         setCursor(Qt::ArrowCursor);
0264         m_scene->enableEvents();
0265     } else { //AI
0266         //lock the view to let the AI do it's magic
0267         setCursor(Qt::WaitCursor);
0268         m_scene->disableEvents();
0269 
0270         QTimer::singleShot(200, this, &KSquaresWindow::aiChooseLine);
0271         setCursor(Qt::ArrowCursor);
0272     }
0273 }
0274 
0275 // testing only
0276 void KSquaresWindow::aiChooseLine()
0277 {
0278     // This can happen when we start a new game and the ai was playing, since we have a
0279     // 200 ms singleShot timer just above, the game may have changed since the timer was shot
0280     if (sGame->currentPlayer()->isHuman())
0281         return;
0282 
0283     aiController ai(sGame->currentPlayerId(), sGame->lines(), sGame->squares(), sGame->boardWidth(), sGame->boardHeight());
0284     sGame->addLineToIndex(ai.chooseLine());
0285 }
0286 
0287 void KSquaresWindow::setupActions()
0288 {
0289     KStandardGameAction::gameNew(this, &KSquaresWindow::gameNew, actionCollection());
0290     QAction *resetGame = KStandardGameAction::restart(this, &KSquaresWindow::gameReset, actionCollection());
0291     resetGame->setStatusTip(i18n("Start a new game with the current settings"));
0292 
0293     KStandardGameAction::highscores(this, &KSquaresWindow::showHighscores, actionCollection());
0294     KStandardGameAction::quit(this, &QWidget::close, actionCollection());
0295 
0296     // Preferences
0297     KStandardAction::preferences(this, &KSquaresWindow::optionsPreferences, actionCollection());
0298 
0299     setupGUI();
0300 }
0301 
0302 void KSquaresWindow::optionsPreferences()
0303 {
0304   auto dialog =
0305       new KConfigDialog(this, QStringLiteral("settings"), Settings::self());
0306 
0307   auto displaySettingsDialog = new QWidget;
0308   ui_prefs_display.setupUi(displaySettingsDialog);
0309   dialog->addPage(displaySettingsDialog, i18n("Display"),
0310                   QStringLiteral("preferences-desktop-display"));
0311 
0312   auto aiSettingsDialog = new QWidget;
0313   ui_prefs_ai.setupUi(aiSettingsDialog);
0314   dialog->addPage(aiSettingsDialog, i18n("Computer Player"),
0315                   QStringLiteral("games-difficult"));
0316 
0317   connect(dialog, &KConfigDialog::settingsChanged, m_view,
0318           &GameBoardView::setBoardSize);
0319   dialog->show();
0320 }
0321