File indexing completed on 2024-04-28 04:02:29

0001 /*
0002     SPDX-FileCopyrightText: 2007 Dmitry Suzdalev <dimsuz@gmail.com>
0003     SPDX-FileCopyrightText: 2010 Brian Croom <brian.s.croom@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #ifndef MINEFIELDITEM_H
0009 #define MINEFIELDITEM_H
0010 
0011 // Qt
0012 #include <QGraphicsObject>
0013 #include <QList>
0014 #include <QPair>
0015 
0016 class KGameRenderer;
0017 class CellItem;
0018 class BorderItem;
0019 
0020 using FieldPos = QPair<int, int>;
0021 
0022 /**
0023  * Graphics item that represents MineField.
0024  * It is composed of many (or little) of CellItems.
0025  * This class is responsible of generation game field
0026  * with given properties (num rows, num cols, num mines) and
0027  * handling resizes
0028  */
0029 class MineFieldItem : public QGraphicsObject
0030 {
0031     Q_OBJECT
0032 public:
0033     /**
0034      * Constructor.
0035      */
0036     explicit MineFieldItem(KGameRenderer* renderer);
0037     /**
0038      * Initializes game field: creates items, places them on positions,
0039      * (re)sets some variables
0040      *
0041      * @param numRows number of rows
0042      * @param numCols number of columns
0043      * @param numMines number of mines
0044      */
0045     void initField( int numRows, int numCols, int numMines );
0046     /**
0047      * Resets mines to the initial state.
0048      */
0049     void resetMines();
0050     /**
0051      * Resizes this graphics item so it fits in given rect
0052      */
0053     void resizeToFitInRect(const QRectF& rect);
0054     /**
0055      * Reimplemented from QGraphicsItem
0056      */
0057     QRectF boundingRect() const override;// reimp
0058     /**
0059      * @return num rows in field
0060      */
0061     int rowCount() const;
0062     /**
0063      * @return num columns in field
0064      */
0065     int columnCount() const;
0066     /**
0067      * @return num mines in field
0068      */
0069     int minesCount() const;
0070 
0071     /**
0072      * Minimal number of free positions on a field
0073      */
0074     static const int MINIMAL_FREE = 10;
0075 
0076 Q_SIGNALS:
0077     void flaggedMinesCountChanged(int);
0078     void firstClickDone();
0079     void gameOver(bool won);
0080 private:
0081     // reimplemented
0082     void mousePressEvent( QGraphicsSceneMouseEvent * ) override;
0083     // reimplemented
0084     void mouseReleaseEvent( QGraphicsSceneMouseEvent * ) override;
0085     // reimplemented
0086     void mouseMoveEvent( QGraphicsSceneMouseEvent * ) override;
0087 
0088     /**
0089      * Returns cell item at (row,col).
0090      * Always use this function instead hand-computing index in m_cells
0091      */
0092     inline CellItem* itemAt(int row, int col) { return m_cells.at( row*m_numCols + col ); }
0093     /**
0094      * Overloaded one, which takes QPair
0095      */
0096     inline CellItem* itemAt( FieldPos pos ) { return itemAt(pos.first,pos.second); }
0097     /**
0098      * Calculates (row,col) from given index in m_cells and returns them in QPair
0099      */
0100     inline FieldPos rowColFromIndex(int idx)
0101         {
0102             int row = idx/m_numCols;
0103             return qMakePair(row, idx - row*m_numCols);
0104         }
0105     /**
0106      * Generates game field ensuring that cell at clickedIdx
0107      * will be empty to allow the player quickly jump into the game.
0108      *
0109      * @param clickedIdx specifies index which should NOT have mine and be empty
0110      */
0111     void generateField(int clickedIdx);
0112     /**
0113      * Returns all adjacent items for item at row, col
0114      */
0115     QList<CellItem*> adjacentItemsFor(int row, int col);
0116     /**
0117      * Returns all valid adjacent row,col pairs for row, col
0118      */
0119     QList<FieldPos> adjacentRowColsFor(int row, int col);
0120     /**
0121      * Checks if player lost the game. Return `true` if lost.
0122      * A `true` return value and a `false` `m_gameOver` indicates that the game is restarted.
0123      */
0124     bool checkLost();
0125     /**
0126      * Checks if player won the game. Return `true` if won.
0127      */
0128     bool checkWon();
0129     /**
0130      * Reveals all unmarked items containing mines
0131      */
0132     void revealAllMines();
0133     /**
0134      * Reimplemented from QGraphicsItem
0135      */
0136     void paint( QPainter * painter, const QStyleOptionGraphicsItem*, QWidget * widget = nullptr ) override;
0137     /**
0138      * Repositions all child cell items upon resizes
0139      */
0140     void adjustItemPositions();
0141     /**
0142      * Reveals all empty cells around cell at (row,col),
0143      * until it found cells with digits (which are also revealed)
0144      */
0145     void revealEmptySpace(int row, int col);
0146     /**
0147      * Sets up border items (positions and properties)
0148      */
0149     void setupBorderItems();
0150     /**
0151      * Changes the flag state of a clicked cell and updates mine count
0152      */
0153     void handleFlag(CellItem* itemUnderMouse);
0154     /**
0155      * Return `true` if the game is finished (and possibly restarted) after the call.
0156      */
0157     bool onItemRevealed(int row, int col);
0158     // overload
0159     bool onItemRevealed(CellItem* item);
0160 
0161     // note: in member functions use itemAt (see above )
0162     // instead of hand-computing index from row & col!
0163     // => not depend on how m_cells is represented
0164     /**
0165      * Array which holds all child cell items
0166      */
0167     QList<CellItem*> m_cells;
0168     /**
0169      * Array which holds border items
0170      */
0171     QList<BorderItem*> m_borders;
0172     /**
0173      * The width and height of minefield cells in scene coordinates
0174      */
0175     int m_cellSize = 1; // dummy init value for non-large boundingRect, non-null because used for divisions
0176     /**
0177      * Number of field rows
0178      */
0179     int m_numRows = 1; // dummy init value for non-large boundingRect, non-null because used for divisions
0180     /**
0181      * Number of field columns
0182      */
0183     int m_numCols = 1; // dummy init value for non-large boundingRect, non-null because used for divisions
0184     /**
0185      * Number of mines in field
0186      */
0187     int m_minesCount;
0188     /**
0189      * Number of flagged mines
0190      */
0191     int m_flaggedMinesCount;
0192     /**
0193      * row and column where mouse was pressed.
0194      * (-1,-1) if it is already released
0195      */
0196     FieldPos m_leftButtonPos;
0197     FieldPos m_midButtonPos;
0198     bool m_firstClick;
0199     bool m_gameOver;
0200     bool m_emulatingMidButton;
0201     int m_numUnrevealed;
0202 
0203     KGameRenderer* m_renderer;
0204 };
0205 
0206 #endif