board.h 10.8 KB
Newer Older
Frederik Schwarzer's avatar
Frederik Schwarzer committed
1
2
/***************************************************************************
 *   KShisen - A japanese game similar to mahjongg                         *
3
4
5
 *   Copyright 1997  Mario Weilguni <mweilguni@sime.com>                   *
 *   Copyright 2002-2004  Dave Corrie <kde@davecorrie.com>                 *
 *   Copyright 2007  Mauricio Piacentini <mauricio@tabuleiro.com>          *
6
 *   Copyright 2009-2016  Frederik Schwarzer <schwarzer@kde.org>           *
Frederik Schwarzer's avatar
Frederik Schwarzer committed
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 *                                                                         *
 *   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, see <http://www.gnu.org/licenses/>. *
 ***************************************************************************/
21

22
23
// KMahjonggLib integration and SVG support for KDE 4: Mauricio Piacentini <mauricio@tabuleiro.com>

24
25
#ifndef KSHISEN_BOARD_H
#define KSHISEN_BOARD_H
26

Frederik Schwarzer's avatar
Frederik Schwarzer committed
27
28
29
30
31
32
33
// STL
#include <vector>

// Qt
#include <QList>
#include <QSize>
#include <QWidget>
34

Frederik Schwarzer's avatar
Frederik Schwarzer committed
35
36
37
38
39
// KDE
#include <KRandomSequence>

// KDEGames
#include <KGameClock>
40
#include <KgSound>
41

42
// LibKMahjongg
Frederik Schwarzer's avatar
Frederik Schwarzer committed
43
#include <kmahjonggbackground.h>
Frederik Schwarzer's avatar
Frederik Schwarzer committed
44
#include <kmahjonggtileset.h>
Frederik Schwarzer's avatar
Frederik Schwarzer committed
45

Frederik Schwarzer's avatar
Frederik Schwarzer committed
46
47
// KShisen
#include "debug.h"
48
49
#include "move.h"
#include "possiblemove.h"
50

51
52
namespace KShisen
{
Frederik Schwarzer's avatar
Frederik Schwarzer committed
53
/**
Frederik Schwarzer's avatar
Frederik Schwarzer committed
54
 * A list of possible moves the player has to choose between
55
 */
Frederik Schwarzer's avatar
Frederik Schwarzer committed
56
using PossibleMoves = QList<PossibleMove>;
57

Frederik Schwarzer's avatar
Frederik Schwarzer committed
58
59
60
/**
 * @brief Class holding the game board and its functions.
 */
61
62
class Board : public QWidget
{
Frederik Schwarzer's avatar
Frederik Schwarzer committed
63
    Q_OBJECT
64

65
public:
Frederik Schwarzer's avatar
Frederik Schwarzer committed
66
    explicit Board(QWidget * parent = nullptr);
67

68
    /// Number of different kinds of tiles in the game.
Frederik Schwarzer's avatar
Frederik Schwarzer committed
69
    static int const nTiles = 42;
70

Frederik Schwarzer's avatar
Frederik Schwarzer committed
71
72
73
    virtual void paintEvent(QPaintEvent * e);
    virtual void mousePressEvent(QMouseEvent * e);
    virtual void resizeEvent(QResizeEvent * e);
74

75
    void setDelay(int);
76
    int delay() const;
77

Frederik Schwarzer's avatar
Frederik Schwarzer committed
78
    /// Returns if undo step is available
79
    bool canUndo() const;
Frederik Schwarzer's avatar
Frederik Schwarzer committed
80
    /// Returns if redo step is available
81
    bool canRedo() const;
Frederik Schwarzer's avatar
Frederik Schwarzer committed
82
    /// Undoes one step
83
    void undo();
Frederik Schwarzer's avatar
Frederik Schwarzer committed
84
85
    /// Redoes one step
    void redo();
86

87
88
    void setSize(int x, int y);
    void resizeBoard();
89

90
    void showHint();
Frederik Schwarzer's avatar
Frederik Schwarzer committed
91
    bool hint_I(PossibleMoves & possibleMoves) const;
92

93
#ifdef DEBUGGING
94
95
96
    void makeHintMove();
    void finish();
    void dumpBoard() const;
Frederik Schwarzer's avatar
Frederik Schwarzer committed
97
    void dumpBoard(const std::vector<int> & board) const;
98
99
#endif

Frederik Schwarzer's avatar
Frederik Schwarzer committed
100
    /// Returns the number of tiles left on the board
101
    int tilesLeft() const;
102
    /// Returns the current game time in seconds
103
    int currentTime() const;
104

105
    /// Returns whether the current game is solvable
106
    bool isSolvable(bool restore); // const?
107

108
    bool solvableFlag() const;
109
    void setSolvableFlag(bool enabled);
110
    bool gravityFlag() const;
111
112
113
    void setGravityFlag(bool enabled);
    void setChineseStyleFlag(bool enabled);
    void setTilesCanSlideFlag(bool enabled);
114

115
    /// Returns possible number of tiles in X direction
Frederik Schwarzer's avatar
Frederik Schwarzer committed
116
    int xTiles() const;
117
    /// Returns possible number of tiles in Y direction
Frederik Schwarzer's avatar
Frederik Schwarzer committed
118
    int yTiles() const;
119
120
    /// Returns overall possible number of tiles in current game board size
    int tiles() const;
121

Frederik Schwarzer's avatar
Frederik Schwarzer committed
122
    /// Resets the game timer
123
    void resetTimer();
Frederik Schwarzer's avatar
Frederik Schwarzer committed
124
    /// Resets the undo history
125
    void resetUndo();
Frederik Schwarzer's avatar
Frederik Schwarzer committed
126
    /// Resets the redo history
127
    void resetRedo();
128
    /// Sets whether there are no matching tiles left
129
    void setGameStuckEnabled(bool enabled);
130
    /// Sets whether the game is over
131
    void setGameOverEnabled(bool enabled);
132
133
    /// Sets whether the game is in cheat mode
    void setCheatModeEnabled(bool enabled);
134
135
136
137

    /** Returns whether the game is over.
     * @return True if game is over, False if game is not over
     */
138
    bool isOver() const;
139
140
141
142

    /** Returns whether the game is in pause mode.
     * @return True if game is paused, False if game is not paused
     */
143
    bool isPaused() const;
144
145
146
147

    /** Returns whether there are still matching tiles left.
     * @return True if there are no matching tiles left, False if there are matching tiles left
     */
148
    bool isStuck() const;
149

150
151
152
153
    /** Returns whether player is in cheat mode.
    * @return True if the player is in cheat mode, False if not
    */
    bool hasCheated() const;
154

155
signals:
156
    void markMatched(); // unused?
157
    void newGameStarted();
158
    void changed();
159
    void tileCountChanged();
160
161
162
    void endOfGame();
    void resized();
    void invalidMove();
163
    void tilesDoNotMatch();
164
165
166
    void selectATile();
    void selectAMove();
    void selectAMatchingTile();
167
    void cheatStatusChanged();
168
169

public slots:
170
    /** Does most of the newGame work.
Raymond Wooninck's avatar
Raymond Wooninck committed
171
172
     * This slot is called from the KShisen::invokeNewGame() signal from KShisen and
     * should call KShisen::newGame again to do the work that cannot be done
173
174
175
176
     * from Board.
     */
    void newGame();

Frederik Schwarzer's avatar
Frederik Schwarzer committed
177
178
    /// Controls the pause mode
    void setPauseEnabled(bool enabled);
179
180
181
182

    /** Enables / disables sounds.
     * @param enabled Whether sound shall be enabled
     */
183
    void setSoundsEnabled(bool enabled);
Frederik Schwarzer's avatar
Frederik Schwarzer committed
184
    /// Loads the game settings
185
    void loadSettings();
Frederik Schwarzer's avatar
Frederik Schwarzer committed
186
    /// Loads the given tileset
187
    bool loadTileset(QString const & pathToTileset);
Frederik Schwarzer's avatar
Frederik Schwarzer committed
188
    /// Loads the given background
189
    bool loadBackground(QString const & pathToBackground);
190

191
192
private slots:
    void undrawConnection();
193

194
195
196
197
protected:
    virtual QSize sizeHint() const;

private: // functions
198
199
200
201
    /** Calculates the board's offset.
     * The board is centred inside the main playing area. xOffset()/yOffset()
     * provide the coordinates of the top-left corner of the board.
     */
Frederik Schwarzer's avatar
Frederik Schwarzer committed
202
203
    int xOffset() const;
    int yOffset() const;
204

205
206
207
    /** Returns the line width to use.
     * The line width should be relative to the tile size, however, if the tile size is too small, keep a minimum line width.
     */
Frederik Schwarzer's avatar
Frederik Schwarzer committed
208
    int lineWidth() const;
209

210
211
212
    void setField(TilePos const & tilePos, int value);
    int field(TilePos const & tilePos) const;
    void updateField(TilePos const & tilePos);
Frederik Schwarzer's avatar
Frederik Schwarzer committed
213
    void showInfoRect(QPainter &, const QString & message);
214
    void drawTiles(QPainter &, QPaintEvent *);
215
    void clearHighlight();
216
217
218
219

    /** Checks if two tiles can match.
     * This is sed for connecting them and for highlighting tiles of the same group.
     */
220
    bool tilesMatch(int tile1, int tile2) const;
221
222

    /** Checks if a path between two tiles can be made with a single line.
223
224
     * @param tilePos1 coordinates of the first tile
     * @param tilePos2 coordinates of the second tile
225
     */
226
    bool canMakePath(TilePos const & tilePos1, TilePos const & tilePos2) const;
227

228
229
230
    /** Checks if the tile at \p tilePos1 can be slid to \p tilePos2.
     * @param tilePos1 coordinates of the slide's initial position
     * @param tilePos2 coordinates of the slide's final position
231
232
     * @param path The movement of the last tile slided will be stored in the path
     */
233
    bool canSlideTiles(TilePos const & tilePos1, TilePos const & tilePos2, Path & path) const;
234
235

    /** Checks if a path between two tiles can be made with 2 or 3 lines.
236
237
    * @param tilePos1 coordinate of the first tile
    * @param tilePos2 coordinate of the second tile
238
239
240
    * @param possibleMoves All the possible moves are stored here
    * @return The number of paths found
    */
241
    int findPath(TilePos const & tilePos1, TilePos const & tilePos2, PossibleMoves & possibleMoves) const;
242
243

    /** Find a path of 1 or 2 segments between tiles.
244
245
     * @param tilePos1 coordinates of the first tile
     * @param tilePos2 coordinates of the second tile
246
247
248
     * @param possibleMoves All the possible moves are stored here
     * @return The number of paths found
     */
249
    int findSimplePath(TilePos const & tilePos1, TilePos const & tilePos2, PossibleMoves & possibleMoves) const;
Frederik Schwarzer's avatar
Frederik Schwarzer committed
250
    void performMove(PossibleMove & possibleMoves);
251
    void performSlide(TilePos const & tilePos, Path const & slide);
252
253
    void reverseSlide(TilePos const & tilePos, int slideX1, int slideY1, int slideX2, int slideY2);
    bool isTileHighlighted(TilePos const & tilePos) const;
254
    void drawConnection();
Frederik Schwarzer's avatar
Frederik Schwarzer committed
255
    void drawPossibleMoves(bool b);
256
257
258
259
260
    /** Calculated the middle coordinates of the given tile position.
     * @param tilePos Tile position
     * @return The middle coordinates of the tile at \p tilePos
     */
    QPoint midCoord(TilePos const & tilePos) const;
261
    void unmarkTile();
262
263
    void marked(TilePos const & tilePos);
    void madeMove(TilePos const & tilePos1, TilePos const & tilePos2, Path slide = Path());
264

265
    /** Applies gravity to all columns.
266
     */
Frederik Schwarzer's avatar
Frederik Schwarzer committed
267
    void applyGravity();
268

Frederik Schwarzer's avatar
Frederik Schwarzer committed
269
270
271
272
273
    /** Returns True if @p tilePos is a valid position on Board.
     * @return Wether @p tiePos is valid.
     */
    bool isValidPos(TilePos const & tilePos) const;

274
275
276
277
278
    /** Returns True if @p tilePos is a valid position on Board including outline.
     * @return Wether @p tiePos is valid.
     */
    bool isValidPosWithOutline(TilePos const & tilePos) const;

279
private:
Frederik Schwarzer's avatar
Frederik Schwarzer committed
280
    KGameClock m_gameClock;
281
282
283
284
285
286

    KMahjonggTileset m_tiles;
    KMahjonggBackground m_background;

    KRandomSequence m_random;

287
288
    QList<Move *> m_undo; ///< Undo history
    QList<Move *> m_redo; ///< Redo history
289
290
291
292
293

    int m_markX;
    int m_markY;
    Path m_connection;
    PossibleMoves m_possibleMoves;
Frederik Schwarzer's avatar
Frederik Schwarzer committed
294
    std::vector<int> m_field; ///< Matrix holding the game board grid
295
296
297
    int m_xTiles;
    int m_yTiles;
    int m_delay;
298
    int m_level;
299
300
    int m_shuffle;

Frederik Schwarzer's avatar
format    
Frederik Schwarzer committed
301
    // The game can be in one of the following states.
302
303
304
305
    enum class GameState { Normal,
                           Paused,
                           Stuck,
                           Over };
306
    GameState m_gameState;
307
    bool m_cheat; ///< Whether the cheat mode is set
308

309
310
311
312
    bool m_gravityFlag; ///< Whether game is played with gravity
    bool m_solvableFlag; ///< Whether game is solvable
    bool m_chineseStyleFlag; ///< Whether game follows Chinese rules
    bool m_tilesCanSlideFlag; ///< Whether tiles can slide when connecting
313
314
315
316
317

    int m_highlightedTile;

    bool m_paintConnection;
    bool m_paintPossibleMoves;
318
    bool m_paintInProgress;
319
320
    TilePos m_tileRemove1;
    TilePos m_tileRemove2;
321
322
    KgSound m_soundPick; ///< Sound object to play when tile is selected
    KgSound m_soundFall; ///< Sound object to play when tiles fall down in gravity mode
323
};
324
}
325

326
#endif // KSHISEN_BOARD_H
327
328
329

// vim: expandtab:tabstop=4:shiftwidth=4
// kate: space-indent on; indent-width 4