diff --git a/ai_kepler.h b/ai_kepler.h index 27b7e00..9034fff 100644 --- a/ai_kepler.h +++ b/ai_kepler.h @@ -1,66 +1,66 @@ /* **************************************************************************** This file is part of the game 'KJumpingCube' Copyright (C) 1998-2000 by Matthias Kiefer Copyright (C) 2012 by Ian Wadham This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **************************************************************************** */ #ifndef AI_KEPLER_H #define AI_KEPLER_H #include "ai_base.h" /** * Class AI_Kepler computes the value of moving a cube. It assists the main AI * class and contains the algorithm from the original KJumpingCube code. * * @short The Kepler AI algorithm */ class AI_Kepler : public AI_Base { public: - QString whoami() Q_DECL_OVERRIDE { return QString ("Kepler"); } // IDW test. + QString whoami() override { return QString ("Kepler"); } // IDW test. /** * The Kepler AI constructor. */ AI_Kepler(); /** * Assess the priority of playing a cube at a particular position. The * highest priority cubes are used by the AI_Main class for look-ahead moves * and calculating the values of the positions reached. The cube to be * assessed has to be neutral or owned by the player who is to move. * * @param index The index-position of the cube to be assessed * @param player The player who is to move * @param neighbors The index-positions of the cube's neighbors (array), * where a value of -1 means no neighbor on that side * @param owners The current owners of the cubes in the box (array) * @param values The current point values of the cubes in the box (array) * @param maxValues The maximum point values of the cubes in the box (array) * * @return The priority of a move (always > 0): moves with priority * 1 are best and those with priority >= HighValue (999) are * worst but may be forced (e.g. when defeat is imminent). */ int assessCube (const int index, const Player player, const int neighbors [4], const Player owners[], - const int values[], const int maxValues[]) const Q_DECL_OVERRIDE; + const int values[], const int maxValues[]) const override; }; #endif // AI_KEPLER_H diff --git a/ai_main.cpp b/ai_main.cpp index ae818e4..d95c8d7 100644 --- a/ai_main.cpp +++ b/ai_main.cpp @@ -1,686 +1,686 @@ /* **************************************************************************** This file is part of the game 'KJumpingCube' Copyright (C) 2012 by Ian Wadham This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **************************************************************************** */ #include "ai_main.h" #include "ai_kepler.h" #include "ai_newton.h" #include "ai_box.h" #include #include "prefs.h" // Use a thread and return the move via a signal. class ThreadedAI : public QThread { Q_OBJECT public: ThreadedAI (AI_Main * ai) : m_ai (ai) { } - void run() Q_DECL_OVERRIDE { + void run() override { int index = m_ai->computeMove(); emit done (index); } /* IDW test. TODO - This is not actually used. Is it needed? * I think AI_Main::stop() sets m_stopped atomically and even * if it does not, the thread will see it next time around. And * the thread is read-only with respect to m_stopped ... * * ATM, KCubeBoxWidget calls AI_Main::stop() not this stop() * and it works ... * * IDW test. TODO - See AI_Main::stop(). It works, but does it need a QMutex? * void stop() { qDebug() << "STOP THREAD REQUESTED"; { QMutexLocker lock (&m_ai->endMutex); m_ai->stop(); } wait(); qDebug() << "STOP THREAD DONE"; } */ signals: void done (int index); private: AI_Main * m_ai; }; const char * text[] = {"TakeOrBeTaken", "EqualOpponent", "CanTake", "OccupyCorner", "OccupyEdge", "OccupyCenter", "CanConsolidate", "CanReachMaximum", "CanExpand", "IncreaseEdge", "IncreaseCenter", "PlayHereAnyway"}; void test (int array[4]) { for (int n = 0; n < 4; n++) { printf ("nb %d %d: ", n, array[n]); } printf ("\n"); } void makeRandomSequence (int nMax, int * seq, KRandomSequence & random) { // Helper routine. Sets up a random sequence of integers 0 to (nMax - 1). for (int n = 0; n < nMax; n++) { seq[n] = n; } int last = nMax; int z, temp; for (int n = 0; n < nMax; n++) { z = random.getLong (last); last--; temp = seq[z]; seq[z] = seq[last]; seq[last] = temp; } return; for (int n = 0; n < nMax; n++) { fprintf (stderr, " %d", seq[n]); } fprintf (stderr, "\n"); } AI_Main::AI_Main (QObject * parent, int side) : AI_Box (parent, side) { qDebug() << "AI_Main CONSTRUCTOR"; m_thread = new ThreadedAI (this); m_randomSeq = new int [m_nCubes]; #if AILog > 0 startStats(); #endif m_AI_Kepler = new AI_Kepler(); m_AI_Newton = new AI_Newton(); setSkill (Prefs::EnumSkill1::Beginner, true, false, Prefs::EnumSkill2::Beginner, true, false); m_stopped = false; m_currentLevel = 0; m_random.setSeed (0); connect(m_thread, &ThreadedAI::done, this, &AI_Main::done); } AI_Main::~AI_Main() { delete m_AI_Kepler; delete m_AI_Newton; delete m_thread; } void AI_Main::setSkill (int skill1, bool kepler1, bool newton1, int skill2, bool kepler2, bool newton2) { m_ai[0] = 0; m_ai[1] = kepler1 ? m_AI_Kepler : m_AI_Newton; m_ai[2] = kepler2 ? m_AI_Kepler : m_AI_Newton; m_ai_skill[0] = 0; m_ai_skill[1] = skill1; m_ai_skill[2] = skill2; m_ai_maxLevel[0] = 0; m_ai_maxLevel[1] = depths[skill1]; m_ai_maxLevel[2] = depths[skill2]; for (int player = 1; player <= 2; player++) { qDebug() << "AI_Main::setSkill: Player" << player << m_ai[player]->whoami() << "skill" << m_ai_skill[player] << "maxLevel" << m_ai_maxLevel[player]; } } void AI_Main::stop() { m_stopped = true; m_thread->wait(); } void AI_Main::getMove (const Player player, const AI_Box * box) { #if AILog > 1 qDebug() << "\nEntering AI_Main::getMove() for player" << player; #endif // These settings are immutable once the thread starts and getMove() returns. // If AI_Main::setSkill() is called when the thread is active, the new values // will not take effect until the next move or hint. m_currentAI = m_ai[player]; m_skill = m_ai_skill[player]; m_maxLevel = m_ai_maxLevel[player]; #if AILog > 0 initStats (player); // IDW test. Statistics collection. #endif #if AILog > 1 qDebug() << tag(0) << "PLAYER" << player << m_currentAI->whoami() << "skill" << m_skill << "max level" << m_maxLevel; #endif m_stopped = false; m_player = player; checkWorkspace (box->side()); initPosition (box, player, true); #if AILog > 1 qDebug() << "INITIAL POSITION"; printBox(); #endif m_thread->start (QThread::IdlePriority); // Run computeMove() on thread. return; } int AI_Main::computeMove() { // IDW test. // Set up a random sequence of integers 0 to (m_nCubes - 1). // IDW test. makeRandomSequence (m_side * m_side, m_randomSeq, m_random); // Start the recursive MiniMax algorithm on a copy of the current cube box. #if AILog > 2 qDebug() << tag(0) << "Calling tryMoves(), level zero, player" << m_player; #endif Move move = tryMoves (m_player, 0); #if AILog > 2 qDebug() << tag(0) << "Returned from tryMoves(), level zero, player" << m_player << "value" << move.val << "simulate" << n_simulate << "assess" << n_assess; #endif #if AILog > 0 saveStats (move); // IDW test. Statistics collection. #endif #if AILog > 1 qDebug() << "=============================================================="; qDebug() << tag(0) << "MOVE" << m_currentMoveNo << "for PLAYER" << m_player << "X" << move.index/m_side << "Y" << move.index%m_side; #endif return (move.index); // Return the best move found, via a signal. } Move AI_Main::tryMoves (Player player, int level) { m_currentLevel = level; // IDW test. To limit qDebug() in findCubesToMove(). long maxValue = -WinnerPlus1; Move bestMove = {-1, -WinnerPlus1}; // Find likely cubes to move. Move * cubesToMove = new Move [m_nCubes]; #if AILog > 3 qDebug() << "FIND CUBES TO MOVE for Player" << player << "from POSITION AT LEVEL" << level; if (level > 0) boxPrint (m_side, (int *) m_owners, m_values); // IDW test. #endif int moves = findCubesToMove (cubesToMove, player, m_owners, m_values, m_maxValues); #if AILog > 2 qDebug() << tag(level) << "Level" << level << "Player" << player << "number of likely moves" << moves; if (level == 0) { for (int n = 0; n < moves; n++) { int v = cubesToMove[n].val; QString s = ""; if ((v > 0) && (v <= 12)) s = QString(text[v-1]); qDebug() << tag(level) << " " << "X" << cubesToMove[n].index/m_side << "Y" << cubesToMove[n].index%m_side << "val" << cubesToMove[n].val << s; } saveLikelyMoves (moves, cubesToMove); // IDW test. } m_currentMove->searchStats->at(level)->n_moves += moves; #endif // IDW TODO - Sort the moves by priority in findCubesToMove() (1 first), // shuffle moves that have the same value (to avoid repetitious openings). // IDW TODO - Apply alpha-beta pruning to the sorted moves. Maybe we can // allow low-priority moves and sacrifices ... for (int n = 0; n < moves; n++) { #if AILog > 2 if (level == 0) qDebug() << "=============================================================="; qDebug() << tag(level) << "TRY" << (n+1) << "at level" << level << "Player" << player << "X"<< cubesToMove[n].index/m_side << "Y" << cubesToMove[n].index%m_side << "val" << cubesToMove[n].val; #endif MoveUndodata undodata; bool won = doMove (player, cubesToMove[n].index, &undodata); #if AILog > 2 n_simulate++; #endif long val; if (won) { // Accept a winning move. bestMove = cubesToMove[n]; bestMove.val = WinnerPlus1 - 1; #if AILog > 2 n_assess++; #endif cubesToMove[n].val = bestMove.val; // IDW test. For debug output. #if AILog > 2 qDebug() << tag(level) << "Player" << player << "wins at level" << level << "move" << cubesToMove[n].index/m_side << cubesToMove[n].index%m_side; #endif undoMove(&undodata); break; } else if (level >= m_maxLevel) { // Stop the recursion. val = m_currentAI->assessPosition (player, m_nCubes, m_owners, m_values); #if AILog > 2 n_assess++; #endif cubesToMove[n].val = val; // IDW test. For debug output. #if AILog > 3 qDebug() << tag(level) << "END RECURSION: Player" << player << "X" << cubesToMove[n].index/m_side << "Y" << cubesToMove[n].index%m_side << "assessment" << val << "on POSITION"; boxPrint (m_side, (int *)(m_owners), m_values);// IDW test. #endif } else { // Switch players. Player opponent = (player == One) ? Two : One; // Do the MiniMax calculation for the next recursion level. /* qDebug() << tag(level) << "CALL tryMoves: Player" << opponent << "level" << level+1; */ Move move = tryMoves (opponent, level + 1); val = move.val; cubesToMove[n].val = val; // IDW test. For debug output. /* qDebug() << tag(level) << "RETURN to level" << level << "Player" << player << "X" << move.index/m_side << "Y" << move.index%m_side << "assessment" << val; */ } if (val > maxValue) { maxValue = val; bestMove = cubesToMove[n]; bestMove.val = val; cubesToMove[n].val = val; // IDW test. For debug output. #if AILog > 2 qDebug() << tag(level) << "NEW MAXIMUM at level" << level << "Player" << player << "X" << bestMove.index/m_side << "Y" << bestMove.index%m_side << "assessment" << val; #endif } Player p = player; undoMove(&undodata); if (p != player) qDebug() << "ERROR: PLAYER CHANGED: from" << p << "to" << player; if (m_stopped) { qDebug() << "STOPPED AT LEVEL" << level; break; } } #if AILog > 2 if (level == 0) { qDebug() << tag(level) << "VALUES OF MOVES - Player" << player << "number of moves" << moves; for (int n = 0; n < moves; n++) { qDebug() << tag(level) << " " << "X" << cubesToMove[n].index/m_side << "Y" << cubesToMove[n].index%m_side << "val" << cubesToMove[n].val; } } #endif delete [] cubesToMove; #if AILog > 2 qDebug(); qDebug() << tag(level) << "BEST MOVE at level" << level << "Player" << player << "X" << bestMove.index/m_side << "Y" << bestMove.index%m_side << "assessment" << bestMove.val; #endif // Apply the MiniMax rule. if (level > 0) { #if AILog > 2 qDebug() << tag(level) << "CHANGE SIGN" << bestMove.val << "to" << -bestMove.val; #endif bestMove.val = - bestMove.val; } return bestMove; } int AI_Main::findCubesToMove (Move * c2m, const Player player, const Player * owners, const int * values, const int * maxValues) { int index, n; int opponent = (player == One) ? Two : One; int moves = 0; int min = VeryHighValue; bool beginner = (m_skill == Prefs::EnumSkill1::Beginner); int secondMin = min; // Set up a random sequence of integers 0 to (m_nCubes - 1). makeRandomSequence (m_nCubes, m_randomSeq, m_random); // Put values on the cubes. int * neighbors = m_neighbors; int val; for (n = 0; n < m_nCubes; n++) { index = m_randomSeq [n]; // Use only cubes that do not belong to the opponent. if (owners[index] == opponent) { continue; } // The beginner selects the cubes with the most pips on them: // other players check the neighbours of each cube. val = beginner ? (5 - values [index]) : m_currentAI->assessCube (index, player, (neighbors + 4 * index), owners, values, maxValues); if (val < min) { secondMin = min; min = val; } else if ((val > min) && (val < secondMin)) { secondMin = val; } // Store the move. c2m[moves].index = index; c2m[moves].val = val; moves++; } #if AILog > 2 if (m_currentLevel == 0) qDebug() << "\nMinimum is" << min << ", second minimum is" << secondMin << "available moves" << moves; #endif if (moves == 0) { // Should not happen? Even bad moves are given a value > 0. qDebug() << "NO LIKELY MOVES AVAILABLE: selecting" << moves << "min" << min; return moves; } int counter = 0; // Find all moves with minimum assessment for (n = 0; n < moves; ++n) { if (c2m[n].val == min) { c2m[counter].index = c2m[n].index; c2m[counter].val = c2m[n].val; counter++; } } // IDW TODO - Finalise the logic for limiting the number of moves tried. // The 1/3 gizmo is to limit searches on an empty cube box. // Should not use secondMin on Expert level? // Should not use secondMin on deeper recursion levels? // Should it all depend on how many moves are at "min"? // Should AI_Newton have overlapping values of move types? // if (true || m_skill == Prefs::EnumSkill1::Average) // IDW test. // if ((counter <= 2) || (m_skill == Prefs::EnumSkill1::Average)) // if ((m_skill == Prefs::EnumSkill1::Average) && if (m_currentMoveNo > (m_nCubes / 3)) { // If board > 1/3 full. for (n = 0; n < moves; ++n) { if (c2m[n].val == secondMin) { c2m[counter].index = c2m[n].index; c2m[counter].val = c2m[n].val; counter++; } } } if (counter != 0) { moves = counter; } // IDW TODO - Can we find a more subtle rule? // If more than maxMoves moves are favorable, take maxMoves random // moves because it will take too long to check more. return qMin (moves, maxBreadth); } void AI_Main::checkWorkspace (int side) { if (m_side != side) { qDebug() << "NEW AI_Box SIZE NEEDED: was" << m_side << "now" << side; delete[] m_randomSeq; resizeBox (side); m_randomSeq = new int [side * side]; } } #if AILog > 0 /* * Debugging methods for the AI. */ void AI_Main::boxPrint (int side, int * owners, int * values) { // Print out a position reached during or after recursion. // fprintf (stderr, "AI_Main::boxPrint (%d, %lu, %lu)\n", // side, (long) owners, (long) values); // Tests push and pop logic. for (int y = 0; y < side; y++) { fprintf (stderr, " "); for (int x = 0; x < side; x++) { int index = x * side + y; if (owners[index] == Nobody) fprintf (stderr, " ."); else fprintf (stderr, " %2d", (owners[index] == One) ? values[index] : -values[index]); } fprintf (stderr, "\n"); } } void AI_Main::startStats() { // IDW test. For debugging. m_currentMoveNo = 0; m_moveStats.clear(); } void AI_Main::postMove (Player player, int index, int side) { // IDW test. Statistics collection. // Used to record a move by a human player. checkWorkspace (side); int x = index / m_side; int y = index % m_side; #if AILog > 1 qDebug() << "AI_Main::postMove(): index" << index << "at" << x << y << "m_side" << m_side; #endif m_maxLevel = m_ai_maxLevel[player]; m_currentMove = new MoveStats [1]; m_currentMoveNo++; m_currentMove->player = player; m_currentMove->moveNo = m_currentMoveNo; m_currentMove->n_simulate = 0; m_currentMove->n_assess = 0; m_currentMove->nLikelyMoves = 0; m_currentMove->likelyMoves = 0; m_currentMove->searchStats = new QList(); m_currentMove->x = x; m_currentMove->y = y; m_currentMove->value = 0; m_moveStats.append (m_currentMove); #if AILog > 1 qDebug() << "=============================================================="; qDebug() << tag(0) << "MOVE" << m_currentMoveNo << "for PLAYER" << player << "X" << x << "Y" << y; qDebug() << "=============================================================="; #endif } void AI_Main::initStats (int player) { // IDW test. For debugging. m_currentMove = new MoveStats [1]; m_currentMoveNo++; m_currentMove->player = (Player) player; m_currentMove->moveNo = m_currentMoveNo; m_currentMove->n_simulate = 0; m_currentMove->n_assess = 0; m_currentMove->nLikelyMoves = 0; m_currentMove->likelyMoves = 0; m_currentMove->searchStats = new QList(); for (int n = 0; n <= m_maxLevel; n++) { SearchStats * s = new SearchStats [1]; s->n_moves = 0; m_currentMove->searchStats->append (s); } n_simulate = 0; n_assess = 0; } void AI_Main::saveLikelyMoves (int nMoves, Move * moves) { Move * m = new Move [nMoves]; m_currentMove->nLikelyMoves = nMoves; m_currentMove->likelyMoves = m; for (int n = 0; n < nMoves; n++) { m [n] = moves [n]; } } void AI_Main::saveStats (Move & move) { // IDW test. For debugging. m_currentMove->x = move.index/m_side; m_currentMove->y = move.index%m_side; m_currentMove->value = move.val; m_currentMove->n_simulate = n_simulate; m_currentMove->n_assess = n_assess; m_moveStats.append (m_currentMove); } void AI_Main::dumpStats() { // IDW test. For debugging. Replay all the moves, with statistics for each. qDebug() << m_moveStats.count() << "MOVES IN THIS GAME"; AI_Box * statsBox = new AI_Box (0, m_side); statsBox->printBox(); for (MoveStats * m : qAsConst(m_moveStats)) { QList l; int nMax = m->searchStats->count(); for (int n = 0; n < nMax; n++) { l.append (m->searchStats->at(n)->n_moves); } qDebug() << ((m->player == 1) ? "p1" : "p2") << "move" << m->moveNo << "X" << m->x << "Y" << m->y << "value" << m->value << m->n_simulate << m->n_assess << l; if (m->nLikelyMoves > 0) { qDebug() << " Number of likely moves" << m->nLikelyMoves; for (int n = 0; n < m->nLikelyMoves; n++) { int v = m->likelyMoves[n].val; QString s = ""; if ((v > 0) && (v <= 12)) s = QString(text[v-1]); qDebug() << " " << "X" << m->likelyMoves[n].index/m_side << "Y" << m->likelyMoves[n].index%m_side << "val" << m->likelyMoves[n].val << s; } delete m->likelyMoves; } bool won = statsBox->doMove (m->player, m->x * m_side + m->y); statsBox->printBox(); qDeleteAll (*(m->searchStats)); delete[] m; } m_moveStats.clear(); delete statsBox; } QString AI_Main::tag (int level) { QString indent (""); indent.fill ('-', 2 * level); indent = indent.prepend (QString::number (level)); indent = indent.leftJustified (2 * m_maxLevel + 1); QString mv = QString::number(m_currentMoveNo).rightJustified(3, '0'); return (QString ("%1 %2 %3")).arg(m_currentMove->player).arg(mv).arg(indent); } #endif /** * This is the default definition of virtual long AI_Base::assessPosition(). * Inheritors of AI_Base can declare and define other versions. */ long AI_Base::assessPosition (const Player player, const int nCubes, const Player owners[], const int values[]) const { int cubesOne = 0; int cubesTwo = 0; int pointsOne = 0; int pointsTwo = 0; Player otherPlayer = (player == One) ? Two : One; int index, points; for (index = 0; index < nCubes; index++) { points = values[index]; if (owners[index] == One) { cubesOne++; pointsOne += points * points; } else if (owners[index] == Two) { cubesTwo++; pointsTwo += points * points; } } if (player == One) { return cubesOne * cubesOne + pointsOne - cubesTwo * cubesTwo - pointsTwo; } else { return cubesTwo * cubesTwo + pointsTwo - cubesOne * cubesOne - pointsOne; } } #include "ai_main.moc" #include "moc_ai_main.cpp" diff --git a/ai_newton.h b/ai_newton.h index 3b0d491..744ffcf 100644 --- a/ai_newton.h +++ b/ai_newton.h @@ -1,66 +1,66 @@ /* **************************************************************************** This file is part of the game 'KJumpingCube' Copyright (C) 1998-2000 by Matthias Kiefer Copyright (C) 2012 by Ian Wadham This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **************************************************************************** */ #ifndef AI_NEWTON_H #define AI_NEWTON_H #include "ai_base.h" /** * Class AI_Newton computes the priority of moving a cube and the value of the * resulting position. It assists the main AI class. * * @short The Newton AI algorithms */ class AI_Newton : public AI_Base { public: - QString whoami() Q_DECL_OVERRIDE { return QString ("Newton"); } // IDW test. + QString whoami() override { return QString ("Newton"); } // IDW test. /** * The Newton AI constructor. */ AI_Newton(); /** * Assess the priority of playing a cube at a particular position. The * highest priority cubes are used by the AI_Main class for look-ahead moves * and calculating the values of the positions reached. The cube to be * assessed has to be neutral or owned by the player who is to move. * * @param index The index-position of the cube to be assessed * @param player The player who is to move * @param neighbors The index-positions of the cube's neighbors (array), * where a value of -1 means no neighbor on that side * @param owners The current owners of the cubes in the box (array) * @param values The current point values of the cubes in the box (array) * @param maxValues The maximum point values of the cubes in the box (array) * * @return The priority of a move (always > 0): moves with priority * 1 are best and those with priority >= HighValue (999) are * worst but may be forced (e.g. when defeat is imminent). */ int assessCube (const int index, const Player player, const int neighbors [4], const Player owners[], - const int values[], const int maxValues[]) const Q_DECL_OVERRIDE; + const int values[], const int maxValues[]) const override; }; #endif // AI_NEWTON_H diff --git a/kcubeboxwidget.h b/kcubeboxwidget.h index 0017ebb..0a44cbb 100644 --- a/kcubeboxwidget.h +++ b/kcubeboxwidget.h @@ -1,148 +1,148 @@ /* **************************************************************************** This file is part of the game 'KJumpingCube' Copyright (C) 1998-2000 by Matthias Kiefer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **************************************************************************** */ #ifndef KCUBEBOXWIDGET_H #define KCUBEBOXWIDGET_H #include #include "ai_globals.h" #include "kcubewidget.h" #include #include #include #include class QTimer; class QLabel; class KCubeBoxWidget : public QWidget { Q_OBJECT public: explicit KCubeBoxWidget (const int dim = 1, QWidget * parent = 0); virtual ~KCubeBoxWidget(); void displayCube (int index, Player owner, int value); void highlightCube (int index, bool highlight); void timedCubeHighlight (int index); int cubeValue (int index) { return cubes.at(index)->value(); } /** * reset cubebox for a new game */ void reset(); /** * Set colors that are used to show owners of the cubes. */ void setColors (); /** * Set the number of cubes in a row or column. If the number has changed, * delete the existing set of cubes and create a new one. */ virtual void setDim (int dim); void makeStatusPixmaps (const int width); const QPixmap & playerPixmap (const int p); /** sets the cursor to an waitcursor */ void setWaitCursor(); /** restores the original cursor */ void setNormalCursor(); bool loadSettings(); signals: void animationDone (int index); void mouseClick (int x, int y); protected: - QSize sizeHint() const Q_DECL_OVERRIDE; + QSize sizeHint() const override; virtual void initCubes(); - void paintEvent (QPaintEvent * event) Q_DECL_OVERRIDE; - void resizeEvent (QResizeEvent * event) Q_DECL_OVERRIDE; + void paintEvent (QPaintEvent * event) override; + void resizeEvent (QResizeEvent * event) override; private: enum AnimationType {None, ComputerMove, Darken, RapidBlink, Scatter}; void init(); QSvgRenderer svg; void makeSVGBackground (const int w, const int h); void makeSVGCubes (const int width); void colorImage (QImage & img, const QColor & c, const int w); void reCalculateGraphics (const int w, const int h); int sWidth; // Width of status pixmaps (used if recoloring). QPixmap status1; // Status-bar pixmaps for players 1 and 2. QPixmap status2; QPixmap background; // Pixmap for background. QList elements; // Pixmaps for cubes, pips and blinking. QColor color1; // Player 1's color. QColor color2; // Player 2's color. QColor color0; // Color for neutral cubes. QPoint topLeft; int cubeSize; int m_side; QList cubes; QTimer *animationTimer; int m_index; AnimationType cascadeAnimation; AnimationType currentAnimation; int animationCount; int animationSteps; int animationTime; QTimer * m_highlightTimer; // Timer for highlighted cube. int m_highlighted; // Cube that has been highlighted. QLabel * m_popup; public: /** * Starts the animation loop. */ void startAnimation (bool cascading, int index); int killAnimation(); void showPopup (const QString & message); void hidePopup(); private: void setPopup(); void scatterDots (int step); private slots: void nextAnimationStep(); void highlightDone(); // Timeout of the highlighted cube. bool checkClick (int x, int y); }; #endif // KCUBEBOXWIDGET_H diff --git a/kcubewidget.h b/kcubewidget.h index e264add..5056707 100644 --- a/kcubewidget.h +++ b/kcubewidget.h @@ -1,107 +1,107 @@ /* **************************************************************************** This file is part of the game 'KJumpingCube' Copyright (C) 1998-2000 by Matthias Kiefer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **************************************************************************** */ #ifndef KCUBEWIDGET_H #define KCUBEWIDGET_H #include #include "ai_globals.h" enum SVGElement {Neutral, Player1, Player2, Pip, BlinkLight, BlinkDark, FirstElement = Neutral, LastElement = BlinkDark}; class QMouseEvent; class QPaintEvent; /** * */ class KCubeWidget : public QFrame { Q_OBJECT public: /** constructs a new KCubeWidget*/ explicit KCubeWidget (QWidget * parent = 0); virtual ~KCubeWidget(); Player owner() { return m_owner; } int value() { return m_value; } void setOwner (Player newOwner); void setValue (int newValue); void setPixmaps (QList * ptr); /** * sets the coordinates of the Cube in a Cubebox; * needed for identification when clicked. */ void setCoordinates (int row, int col, int limit); /** enables or disables possibility to click a cube*/ static void enableClicks(bool flag); void setLight() { blinking = Light; update(); } void setDark() { blinking = Dark; update(); } void setNeutral() { blinking = None; update(); } bool isNeutral() { return (blinking == None); } void shrink (qreal scale); void expand (qreal scale); void migrateDot (int rowDiff, int colDiff, int step, Player player); public slots: /** resets the Cube to default values */ virtual void reset(); /** shows changed colors*/ virtual void updateColors(); signals: void clicked (int row, int column); protected: /** checks, if mouseclick was inside this cube*/ - void mouseReleaseEvent(QMouseEvent*) Q_DECL_OVERRIDE; + void mouseReleaseEvent(QMouseEvent*) override; /** refreshes the contents of the Cube */ - void paintEvent(QPaintEvent*) Q_DECL_OVERRIDE; + void paintEvent(QPaintEvent*) override; private: int m_row; int m_col; int m_limit; Player m_owner; int m_value; QList * pixmaps; enum Blink {None, Light, Dark}; Blink blinking; static bool _clicksAllowed; int migrating; qreal m_rowDiff; qreal m_colDiff; Player m_player; qreal m_scale; qreal m_opacity; }; #endif // KCUBEWIDGET_H diff --git a/kjumpingcube.h b/kjumpingcube.h index eb1bf48..638f580 100644 --- a/kjumpingcube.h +++ b/kjumpingcube.h @@ -1,75 +1,75 @@ /* **************************************************************************** This file is part of the game 'KJumpingCube' Copyright (C) 1998-2000 by Matthias Kiefer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **************************************************************************** */ #ifndef KJUMPINGCUBE_H #define KJUMPINGCUBE_H #include #include #include class QAction; class KCubeBoxWidget; class QPushButton; /** * This class serves as the main window for KJumpingCube. It handles the * menus, toolbars, and status bars. * * @short Main window class * @author Matthias Kiefer * @version 0.7.2 */ class KJumpingCube : public KXmlGuiWindow { Q_OBJECT public: /** Default Constructor */ KJumpingCube(); public slots: void setAction (const Action a, const bool onOff); protected: /// To make sure all activity ceases before closing. - bool queryClose() Q_DECL_OVERRIDE; + bool queryClose() override; private: Game * m_game; KCubeBoxWidget * m_view; QLabel *currentPlayer; QAction *undoAction, *redoAction, *stopAction, *hintAction; void initKAction(); QPushButton * actionButton; QString buttonLook; private slots: void changePlayerColor (int newPlayer); void changeButton (bool enabled, bool stop = false, const QString & caption = QString()); void statusMessage (const QString & message, bool timed); }; #endif // KJUMPINGCUBE_H