File indexing completed on 2024-04-21 04:04:02

0001 /*
0002     KShisen - A japanese game similar to Mahjongg
0003     SPDX-FileCopyrightText: 1997 Mario Weilguni <mweilguni@sime.com>
0004     SPDX-FileCopyrightText: 2002-2004 Dave Corrie <kde@davecorrie.com>
0005     SPDX-FileCopyrightText: 2007 Mauricio Piacentini <mauricio@tabuleiro.com>
0006     SPDX-FileCopyrightText: 2009-2016 Frederik Schwarzer <schwarzer@kde.org>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 // KMahjonggLib integration and SVG support for KDE 4: Mauricio Piacentini <mauricio@tabuleiro.com>
0012 
0013 #ifndef KSHISEN_BOARD_H
0014 #define KSHISEN_BOARD_H
0015 
0016 // STL
0017 #include <memory>
0018 #include <vector>
0019 
0020 // Qt
0021 #include <QList>
0022 #include <QSize>
0023 #include <QWidget>
0024 #include <QRandomGenerator>
0025 
0026 // KDEGames
0027 #include <KGameClock>
0028 #include <KGameSound>
0029 
0030 // LibKMahjongg
0031 #include <KMahjonggBackground>
0032 #include <KMahjonggTileset>
0033 
0034 // KShisen
0035 #include "debug.h"
0036 #include "move.h"
0037 #include "possiblemove.h"
0038 
0039 namespace KShisen
0040 {
0041 /**
0042  * A list of possible moves the player has to choose between
0043  */
0044 using PossibleMoves = QList<PossibleMove>;
0045 
0046 /**
0047  * @brief Class holding the game board and its functions.
0048  */
0049 class Board : public QWidget
0050 {
0051     Q_OBJECT
0052 
0053 public:
0054     explicit Board(QWidget * parent = nullptr);
0055 
0056     /// Number of different kinds of tiles in the game.
0057     static int constexpr nTiles = 42;
0058 
0059     void paintEvent(QPaintEvent * e) override;
0060     void mousePressEvent(QMouseEvent * e) override;
0061     void resizeEvent(QResizeEvent * e) override;
0062 
0063     void setDelay(int);
0064     int delay() const;
0065 
0066     /// Returns if undo step is available
0067     bool canUndo() const;
0068     /// Returns if redo step is available
0069     bool canRedo() const;
0070     /// Undoes one step
0071     void undo();
0072     /// Redoes one step
0073     void redo();
0074 
0075     void setSize(int x, int y);
0076     void resizeBoard();
0077 
0078     void showHint();
0079     bool pathFoundBetweenMatchingTiles(PossibleMoves & possibleMoves) const;
0080 
0081 #ifdef DEBUGGING
0082     void makeHintMove();
0083     void finish();
0084     void dumpBoard() const;
0085     void dumpBoard(const std::vector<int> & field) const;
0086 #endif
0087 
0088     /// Returns the number of tiles left on the board
0089     int tilesLeft() const;
0090     /// Returns the current game time in seconds
0091     int currentTime() const;
0092 
0093     /// Returns whether the current game is solvable
0094     bool isSolvable(bool restore); // const?
0095 
0096     bool solvableFlag() const;
0097     void setSolvableFlag(bool enabled);
0098     bool showUnsolvableMessageFlag() const;
0099     void setShowUnsolvableMessageFlag(bool enabled);
0100     bool gravityFlag() const;
0101     void setGravityFlag(bool enabled);
0102     void setChineseStyleFlag(bool enabled);
0103     void setTilesCanSlideFlag(bool enabled);
0104 
0105     /// Returns possible number of tiles in X direction
0106     int xTiles() const;
0107     /// Returns possible number of tiles in Y direction
0108     int yTiles() const;
0109     /// Returns overall possible number of tiles in current game board size
0110     int tiles() const;
0111 
0112     /// Resets the game timer
0113     void resetTimer();
0114     /// Resets the undo history
0115     void resetUndo();
0116     /// Resets the redo history
0117     void resetRedo();
0118     /// Sets whether there are no matching tiles left
0119     void setGameStuckEnabled(bool enabled);
0120     /// Sets whether the game is over
0121     void setGameOverEnabled(bool enabled);
0122     /// Sets whether the game is in cheat mode
0123     void setCheatModeEnabled(bool enabled);
0124 
0125     /** Returns whether the game is over.
0126      * @return True if game is over, False if game is not over
0127      */
0128     bool isOver() const;
0129 
0130     /** Returns whether the game is in pause mode.
0131      * @return True if game is paused, False if game is not paused
0132      */
0133     bool isPaused() const;
0134 
0135     /** Returns whether there are still matching tiles left.
0136      * @return True if there are no matching tiles left, False if there are matching tiles left
0137      */
0138     bool isStuck() const;
0139 
0140     /** Returns whether player is in cheat mode.
0141     * @return True if the player is in cheat mode, False if not
0142     */
0143     bool hasCheated() const;
0144 
0145 Q_SIGNALS:
0146     void markMatched(); // unused?
0147     void newGameStarted();
0148     void changed();
0149     void tileCountChanged();
0150     void endOfGame();
0151     void resized();
0152     void invalidMove();
0153     void tilesDoNotMatch();
0154     void selectATile();
0155     void selectAMove();
0156     void selectAMatchingTile();
0157     void cheatStatusChanged();
0158 
0159 public Q_SLOTS:
0160     /** Does most of the newGame work.
0161      * This slot is called from the KShisen::invokeNewGame() signal from KShisen and
0162      * should call KShisen::newGame again to do the work that cannot be done
0163      * from Board.
0164      */
0165     void newGame();
0166 
0167     /// Controls the pause mode
0168     void setPauseEnabled(bool enabled);
0169 
0170     /** Enables / disables sounds.
0171      * @param enabled Whether sound shall be enabled
0172      */
0173     void setSoundsEnabled(bool enabled);
0174     /// Loads the game settings
0175     void loadSettings();
0176     /// Loads the given tileset
0177     bool loadTileset(QString const & pathToTileset);
0178     /// Loads the given background
0179     bool loadBackground(QString const & pathToBackground);
0180 
0181 private Q_SLOTS:
0182     void undrawConnection();
0183 
0184 protected:
0185     QSize sizeHint() const override;
0186 
0187 private: // functions
0188     /** Calculates the board's offset.
0189      * The board is centred inside the main playing area. xOffset()/yOffset()
0190      * provide the coordinates of the top-left corner of the board.
0191      */
0192     int xOffset() const;
0193     int yOffset() const;
0194 
0195     /** Returns the line width to use.
0196      * The line width should be relative to the tile size, however, if the tile size is too small, keep a minimum line width.
0197      */
0198     int lineWidth() const;
0199 
0200     /** Puts a tile of type @p value to the given position @p tilePos.
0201      * @param tilePos Position to be modified.
0202      * @param value Type of the tile to place.
0203      */
0204     void setField(TilePos tilePos, int value);
0205     /** Returns the kind of tile residing at the given position @p tilePos
0206      * @param TilePos Position to look at.
0207      * @return Type of the tile.
0208      */
0209     int field(TilePos tilePos) const;
0210     /** Repaints the area of the given @p TilePos.
0211      * @param TilePos Position of the tile to repaint.
0212      */
0213     void repaintTile(TilePos tilePos);
0214     void showInfoRect(QPainter &, const QString & message);
0215     void drawTiles(QPainter &, QPaintEvent *);
0216     void clearHighlight();
0217 
0218     /** Checks if two tiles can match.
0219      * This is used for connecting them and for highlighting tiles of the same group.
0220      * @param tile1 type of the first tile
0221      * @param tile2 type of the second tile
0222      */
0223     bool tilesMatch(int tile1, int tile2) const;
0224 
0225     /** Checks if a path between two tiles can be made with a single line.
0226      * @param tilePos1 coordinates of the first tile
0227      * @param tilePos2 coordinates of the second tile
0228      */
0229     bool canMakePath(TilePos tilePos1, TilePos tilePos2) const;
0230 
0231     /** Checks if the tile at \p tilePos1 can be slid to \p tilePos2.
0232      * @param tilePos1 coordinates of the slide's initial position
0233      * @param tilePos2 coordinates of the slide's final position
0234      * @param slide The movement of the last tile slid will be stored in @p slide
0235      */
0236     bool canSlideTiles(TilePos tilePos1, TilePos tilePos2, Slide & slide) const;
0237 
0238     /** Checks if a path between two tiles can be made with 2 or 3 lines.
0239     * @param tilePos1 coordinates of the first tile
0240     * @param tilePos2 coordinates of the second tile
0241     * @param possibleMoves All the possible moves are stored here
0242     * @return The number of paths found
0243     */
0244     int findPath(TilePos tilePos1, TilePos tilePos2, PossibleMoves & possibleMoves) const;
0245 
0246     /** Find a path of 1 or 2 segments between tiles.
0247      * @param tilePos1 coordinates of the first tile
0248      * @param tilePos2 coordinates of the second tile
0249      * @param possibleMoves all the possible moves are stored here
0250      * @return The number of paths found
0251      */
0252     int findSimplePath(TilePos tilePos1, TilePos tilePos2, PossibleMoves & possibleMoves) const;
0253     void performMove(PossibleMove & possibleMoves);
0254     void performSlide(TilePos tilePos, Slide const & slide);
0255     void reverseSlide(TilePos tilePos, Slide const & slide);
0256     bool isTileHighlighted(TilePos tilePos) const;
0257     void drawConnection();
0258     void drawPossibleMoves(bool b);
0259     /** Calculated the middle coordinates of the given tile position.
0260      * @param tilePos tile position
0261      * @return The middle coordinates of the tile at \p tilePos
0262      */
0263     QPoint midCoord(TilePos tilePos) const;
0264     void unmarkTile();
0265     void marked(TilePos tilePos);
0266     void madeMove(TilePos tilePos1, TilePos tilePos2, Slide slide = Slide());
0267 
0268     /// Applies gravity to all columns.
0269     void applyGravity();
0270 
0271     /** Returns True if @p tilePos is a valid position on Board.
0272      * @return Whether @p tiePos is valid.
0273      */
0274     bool isValidPos(TilePos tilePos) const;
0275 
0276     /** Returns True if @p tilePos is a valid position on Board including outline.
0277      * @return Whether @p tiePos is valid.
0278      */
0279     bool isValidPosWithOutline(TilePos tilePos) const;
0280 
0281     /** Returns True if @p tile is of kind FLOWERS.
0282      * @return Whether @p tile is within the range of the Flower tiles.
0283      */
0284     bool isTileFlower(int tile) const;
0285     /** Returns True if @p tile is of kind SEASONS.
0286      * @return Whether @p tile is within the range of the Seasons tiles.
0287      */
0288     bool isTileSeason(int tile) const;
0289 
0290 private:
0291     KGameClock m_gameClock{};
0292 
0293     KMahjonggTileset m_tiles{};
0294     KMahjonggBackground m_background{};
0295 
0296     QRandomGenerator m_random{};
0297 
0298     std::list<std::unique_ptr<Move>> m_undo{}; ///< Undo history
0299     std::list<std::unique_ptr<Move>> m_redo{}; ///< Redo history
0300 
0301     int m_markX{0};
0302     int m_markY{0};
0303     Path m_connection{};
0304     PossibleMoves m_possibleMoves{};
0305     std::vector<int> m_field{}; ///< Matrix holding the game board grid
0306     int m_xTiles{};
0307     int m_yTiles{};
0308     int m_delay{};
0309     int m_level{};
0310     int m_shuffle{};
0311 
0312     // The game can be in one of the following states.
0313     enum class GameState { Normal,
0314                            Paused,
0315                            Stuck,
0316                            Over };
0317     GameState m_gameState{GameState::Normal};
0318     bool m_cheat{}; ///< Whether the cheat mode is set
0319 
0320     bool m_gravityFlag{true}; ///< Whether game is played with gravity
0321     bool m_solvableFlag{}; ///< Whether game is solvable
0322     bool m_showUnsolvableMessageFlag{}; ///< Whether "game unsolvable" message is shown
0323     bool m_chineseStyleFlag{}; ///< Whether game follows Chinese rules
0324     bool m_tilesCanSlideFlag{}; ///< Whether tiles can slide when connecting
0325 
0326     int m_highlightedTile{-1};
0327 
0328     bool m_paintConnection{};
0329     bool m_paintPossibleMoves{};
0330     bool m_paintInProgress{};
0331     TilePos m_tileRemove1{};
0332     TilePos m_tileRemove2{};
0333     KGameSound m_soundPick; ///< Sound object to play when tile is selected
0334     KGameSound m_soundFall; ///< Sound object to play when tiles fall down in gravity mode
0335 };
0336 } // namespace KShisen
0337 
0338 #endif // KSHISEN_BOARD_H
0339 
0340 // vim: expandtab:tabstop=4:shiftwidth=4
0341 // kate: space-indent on; indent-width 4