summaryrefslogtreecommitdiff
path: root/noncore/games/minesweep/minefield.cpp
Unidiff
Diffstat (limited to 'noncore/games/minesweep/minefield.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/games/minesweep/minefield.cpp623
1 files changed, 623 insertions, 0 deletions
diff --git a/noncore/games/minesweep/minefield.cpp b/noncore/games/minesweep/minefield.cpp
new file mode 100644
index 0000000..be2f9a3
--- a/dev/null
+++ b/noncore/games/minesweep/minefield.cpp
@@ -0,0 +1,623 @@
1/**********************************************************************
2** Copyright (C) 2000 Trolltech AS. All rights reserved.
3**
4** This file is part of Qtopia Environment.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20#include "minefield.h"
21
22#include <qpe/config.h>
23
24#include <qpainter.h>
25#include <qdrawutil.h>
26#include <qpixmap.h>
27#include <qimage.h>
28#include <qtimer.h>
29
30#include <stdlib.h>
31
32static const char *pix_flag[]={
33"13 13 3 1",
34"# c #000000",
35"x c #ff0000",
36". c None",
37".............",
38".............",
39".....#xxxxxx.",
40".....#xxxxxx.",
41".....#xxxxxx.",
42".....#xxxxxx.",
43".....#.......",
44".....#.......",
45".....#.......",
46".....#.......",
47"...#####.....",
48"..#######....",
49"............."};
50
51static const char *pix_mine[]={
52"13 13 3 1",
53"# c #000000",
54". c None",
55"a c #ffffff",
56"......#......",
57"......#......",
58"..#.#####.#..",
59"...#######...",
60"..##aa#####..",
61"..##aa#####..",
62"#############",
63"..#########..",
64"..#########..",
65"...#######...",
66"..#.#####.#..",
67"......#......",
68"......#......"};
69
70class Mine : public QTableItem
71{
72public:
73 enum MineState {
74 Hidden = 0,
75 Empty,
76 Mined,
77 Flagged,
78#ifdef MARK_UNSURE
79 Unsure,
80#endif
81 Exploded,
82 Wrong
83 };
84
85 Mine( QTable* );
86 void paint( QPainter * p, const QColorGroup & cg, const QRect & cr, bool selected );
87 EditType editType() const { return Never; }
88 QSize sizeHint() const { return QSize( 12, 12 ); }
89
90 void activate( bool sure = TRUE );
91 void setHint( int );
92
93 void setState( MineState );
94 MineState state() const { return st; }
95
96 bool isMined() const { return mined; }
97 void setMined( bool m ) { mined = m; }
98
99 static void paletteChange();
100
101private:
102 bool mined;
103 int hint;
104
105 MineState st;
106
107 static QPixmap* knownField;
108 static QPixmap* unknownField;
109 static QPixmap* flag_pix;
110 static QPixmap* mine_pix;
111};
112
113QPixmap* Mine::knownField = 0;
114QPixmap* Mine::unknownField = 0;
115QPixmap* Mine::flag_pix = 0;
116QPixmap* Mine::mine_pix = 0;
117
118Mine::Mine( QTable *t )
119: QTableItem( t, Never, QString::null )
120{
121 mined = FALSE;
122 st = Hidden;
123 hint = 0;
124}
125
126void Mine::activate( bool sure )
127{
128 if ( !sure ) {
129 switch ( st ) {
130 case Hidden:
131 setState( Flagged );
132 break;
133 case Flagged:
134#ifdef MARK_UNSURE
135 setState( Unsure );
136 break;
137 case Unsure:
138#endif
139 setState( Hidden );
140 default:
141 break;
142 }
143 } else if ( st == Flagged ) {
144 return;
145 } else {
146 if ( mined ) {
147 setState( Exploded );
148 } else {
149 setState( Empty );
150 }
151 }
152}
153
154void Mine::setState( MineState s )
155{
156 st = s;
157}
158
159void Mine::setHint( int h )
160{
161 hint = h;
162}
163
164void Mine::paletteChange()
165{
166 delete knownField;
167 knownField = 0;
168 delete unknownField;
169 unknownField = 0;
170 delete mine_pix;
171 mine_pix = 0;
172 delete flag_pix;
173 flag_pix = 0;
174}
175
176void Mine::paint( QPainter* p, const QColorGroup &cg, const QRect& cr, bool )
177{
178 if ( !knownField ) {
179 knownField = new QPixmap( cr.width(), cr.height() );
180 QPainter pp( knownField );
181 QBrush br( cg.button().dark(115) );
182 qDrawWinButton( &pp, QRect(0,0,cr.width(), cr.height())/*cr*/, cg, TRUE, &br );
183 }
184
185 const int pmmarg=cr.width()/5;
186
187 if ( !unknownField ) {
188 unknownField = new QPixmap( cr.width(), cr.height() );
189 QPainter pp( unknownField );
190 QBrush br( cg.button() );
191 qDrawWinButton( &pp, QRect(0,0,cr.width(), cr.height())/*cr*/, cg, FALSE, &br );
192 }
193
194 if ( !flag_pix ) {
195 flag_pix = new QPixmap( cr.width()-pmmarg*2, cr.height()-pmmarg*2 );
196 flag_pix->convertFromImage( QImage(pix_flag).smoothScale(cr.width()-pmmarg*2, cr.height()-pmmarg*2) );
197 }
198
199 if ( !mine_pix ) {
200 mine_pix = new QPixmap( cr.width()-pmmarg*2, cr.height()-pmmarg*2 );
201 mine_pix->convertFromImage( QImage(pix_mine).smoothScale(cr.width()-pmmarg*2, cr.height()-pmmarg*2) );
202 }
203
204 p->save();
205
206 switch(st) {
207 case Hidden:
208 p->drawPixmap( 0, 0, *unknownField );
209 break;
210 case Empty:
211 p->drawPixmap( 0, 0, *knownField );
212 if ( hint > 0 ) {
213 switch( hint ) {
214 case 1:
215 p->setPen( blue );
216 break;
217 case 2:
218 p->setPen( green );
219 case 3:
220 p->setPen( red );
221 break;
222 default:
223 p->setPen( darkMagenta );
224 break;
225 }
226 p->drawText( QRect( 0, 0, cr.width(), cr.height() ), AlignHCenter | AlignVCenter, QString().setNum( hint ) );
227 }
228 break;
229 case Mined:
230 p->drawPixmap( 0, 0, *knownField );
231 p->drawPixmap( pmmarg, pmmarg, *mine_pix );
232 break;
233 case Exploded:
234 p->drawPixmap( 0, 0, *knownField );
235 p->drawPixmap( pmmarg, pmmarg, *mine_pix );
236 p->setPen( red );
237 p->drawText( QRect( 0, 0, cr.width(), cr.height() ), AlignHCenter | AlignVCenter, "X" );
238 break;
239 case Flagged:
240 p->drawPixmap( 0, 0, *unknownField );
241 p->drawPixmap( pmmarg, pmmarg, *flag_pix );
242 break;
243#ifdef MARK_UNSURE
244 case Unsure:
245 p->drawPixmap( 0, 0, *unknownField );
246 p->drawText( QRect( 0, 0, cr.width(), cr.height() ), AlignHCenter | AlignVCenter, "?" );
247 break;
248#endif
249 case Wrong:
250 p->drawPixmap( 0, 0, *unknownField );
251 p->drawPixmap( pmmarg, pmmarg, *flag_pix );
252 p->setPen( red );
253 p->drawText( QRect( 0, 0, cr.width(), cr.height() ), AlignHCenter | AlignVCenter, "X" );
254 break;
255 }
256
257 p->restore();
258}
259
260/*
261 MineField implementation
262*/
263
264MineField::MineField( QWidget* parent, const char* name )
265: QTable( parent, name )
266{
267 setState( GameOver );
268 setShowGrid( FALSE );
269 horizontalHeader()->hide();
270 verticalHeader()->hide();
271 setTopMargin( 0 );
272 setLeftMargin( 0 );
273
274 setSizePolicy( QSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum ) );
275
276 setSelectionMode( QTable::NoSelection );
277 setFocusPolicy( QWidget::NoFocus );
278
279 setCurrentCell( -1, -1 );
280
281 connect( this, SIGNAL( pressed( int, int, int, const QPoint& ) ), this, SLOT( cellPressed( int, int ) ) );
282 connect( this, SIGNAL( clicked( int, int, int, const QPoint& ) ), this, SLOT( cellClicked( int, int ) ) );
283
284 holdTimer = new QTimer( this );
285 connect( holdTimer, SIGNAL( timeout() ), this, SLOT( held() ) );
286
287 flagAction = NoAction;
288 ignoreClick = FALSE;
289 currRow = currCol = 0;
290 minecount=0;
291 mineguess=0;
292 nonminecount=0;
293}
294
295MineField::~MineField()
296{
297}
298
299void MineField::setState( State st )
300{
301 stat = st;
302}
303
304
305void MineField::setup( int level )
306{
307 lev = level;
308 setState( Waiting );
309 viewport()->setUpdatesEnabled( FALSE );
310
311 int cellsize;
312
313 int x;
314 int y;
315 for ( x = 0; x < numCols(); x++ )
316 for ( y = 0; y < numRows(); y++ )
317 clearCell( y, x );
318
319 switch( lev ) {
320 case 1:
321 setNumRows( 9 );
322 setNumCols( 9 );
323 minecount = 12;
324 cellsize = 21;
325 break;
326 case 2:
327 setNumRows( 16 );
328 setNumCols( 16 );
329 minecount = 45;
330 cellsize = 14;
331 break;
332 case 3:
333 setNumRows( 18 );
334 setNumCols( 18 );
335 minecount = 66 ;
336 cellsize = 12;
337 break;
338 }
339 nonminecount = numRows()*numCols() - minecount;
340 mineguess = minecount;
341 emit mineCount( mineguess );
342 Mine::paletteChange();
343
344 for ( y = 0; y < numRows(); y++ )
345 setRowHeight( y, cellsize );
346 for ( x = 0; x < numCols(); x++ )
347 setColumnWidth( x, cellsize );
348 for ( x = 0; x < numCols(); x++ )
349 for ( y = 0; y < numRows(); y++ )
350 setItem( y, x, new Mine( this ) );
351
352 updateGeometry();
353 viewport()->setUpdatesEnabled( TRUE );
354 viewport()->repaint( TRUE );
355}
356
357
358void MineField::placeMines()
359{
360 int mines = minecount;
361 while ( mines ) {
362 int col = int((double(rand()) / double(RAND_MAX)) * numCols());
363 int row = int((double(rand()) / double(RAND_MAX)) * numRows());
364
365 Mine* mine = (Mine*)item( row, col );
366
367 if ( mine && !mine->isMined() && mine->state() == Mine::Hidden ) {
368 mine->setMined( TRUE );
369 mines--;
370 }
371 }
372}
373
374void MineField::paintFocus( QPainter*, const QRect& )
375{
376}
377
378void MineField::viewportMousePressEvent( QMouseEvent* e )
379{
380 QTable::viewportMousePressEvent( e );
381}
382
383void MineField::viewportMouseReleaseEvent( QMouseEvent* e )
384{
385 QTable::viewportMouseReleaseEvent( e );
386 if ( flagAction == FlagNext ) {
387 flagAction = NoAction;
388 }
389}
390
391void MineField::keyPressEvent( QKeyEvent* e )
392{
393#if defined(Q_WS_QWS) || defined(_WS_QWS_)
394 flagAction = ( e->key() == Key_Up ) ? FlagOn : NoAction;
395#else
396 flagAction = ( ( e->state() & ShiftButton ) == ShiftButton ) ? FlagOn : NoAction;
397#endif
398}
399
400void MineField::keyReleaseEvent( QKeyEvent* )
401{
402 flagAction = NoAction;
403}
404
405int MineField::getHint( int row, int col )
406{
407 int hint = 0;
408 for ( int c = col-1; c <= col+1; c++ )
409 for ( int r = row-1; r <= row+1; r++ ) {
410 Mine* mine = (Mine*)item( r, c );
411 if ( mine && mine->isMined() )
412 hint++;
413 }
414
415 return hint;
416}
417
418void MineField::setHint( Mine* mine )
419{
420 if ( !mine )
421 return;
422
423 int row = mine->row();
424 int col = mine->col();
425 int hint = getHint( row, col );
426
427 if ( !hint ) {
428 for ( int c = col-1; c <= col+1; c++ )
429 for ( int r = row-1; r <= row+1; r++ ) {
430 Mine* mine = (Mine*)item( r, c );
431 if ( mine && mine->state() == Mine::Hidden ) {
432 mine->activate( TRUE );
433 nonminecount--;
434 setHint( mine );
435 updateCell( r, c );
436 }
437 }
438 }
439
440 mine->setHint( hint );
441 updateCell( row, col );
442}
443
444/*
445 state == Waiting means no "hold"
446
447
448*/
449void MineField::cellPressed( int row, int col )
450{
451 if ( state() == GameOver )
452 return;
453 currRow = row;
454 currCol = col;
455 if ( state() == Playing )
456 holdTimer->start( 150, TRUE );
457}
458
459void MineField::held()
460{
461 flagAction = FlagNext;
462 updateMine( currRow, currCol );
463 ignoreClick = TRUE;
464}
465
466/*
467 Only place mines after first click, since it is pointless to
468 kill the player before the game has started.
469*/
470
471void MineField::cellClicked( int row, int col )
472{
473 if ( state() == GameOver )
474 return;
475 if ( state() == Waiting ) {
476 Mine* mine = (Mine*)item( row, col );
477 if ( !mine )
478 return;
479 mine->setState( Mine::Empty );
480 nonminecount--;
481 placeMines();
482 setState( Playing );
483 emit gameStarted();
484 updateMine( row, col );
485 } else { // state() == Playing
486 holdTimer->stop();
487 if ( ignoreClick )
488 ignoreClick = FALSE;
489 else
490 updateMine( row, col );
491 }
492}
493
494void MineField::updateMine( int row, int col )
495{
496 Mine* mine = (Mine*)item( row, col );
497 if ( !mine )
498 return;
499
500 bool wasFlagged = mine->state() == Mine::Flagged;
501 bool wasEmpty = mine->state() == Mine::Empty;
502
503 mine->activate( flagAction == NoAction );
504
505 if ( mine->state() == Mine::Exploded ) {
506 emit gameOver( FALSE );
507 setState( GameOver );
508 return;
509 } else if ( mine->state() == Mine::Empty ) {
510 setHint( mine );
511 if ( !wasEmpty )
512 nonminecount--;
513 }
514
515 if ( flagAction != NoAction ) {
516 if ( mine->state() == Mine::Flagged ) {
517 --mineguess;
518 emit mineCount( mineguess );
519 if ( mine->isMined() )
520 --minecount;
521 } else if ( wasFlagged ) {
522 ++mineguess;
523 emit mineCount( mineguess );
524 if ( mine->isMined() )
525 ++minecount;
526 }
527 }
528
529 updateCell( row, col );
530
531 if ( !minecount && !mineguess || !nonminecount ) {
532 emit gameOver( TRUE );
533 setState( GameOver );
534 }
535}
536
537void MineField::showMines()
538{
539 for ( int c = 0; c < numCols(); c++ )
540 for ( int r = 0; r < numRows(); r++ ) {
541 Mine* mine = (Mine*)item( r, c );
542 if ( !mine )
543 continue;
544 if ( mine->isMined() && mine->state() == Mine::Hidden )
545 mine->setState( Mine::Mined );
546 if ( !mine->isMined() && mine->state() == Mine::Flagged )
547 mine->setState( Mine::Wrong );
548
549 updateCell( r, c );
550 }
551}
552
553void MineField::paletteChange( const QPalette &o )
554{
555 Mine::paletteChange();
556 QTable::paletteChange( o );
557}
558
559void MineField::writeConfig(Config& cfg) const
560{
561 cfg.setGroup("Field");
562 cfg.writeEntry("Level",lev);
563 QString grid="";
564 if ( stat == Playing ) {
565 for ( int x = 0; x < numCols(); x++ )
566 for ( int y = 0; y < numRows(); y++ ) {
567 char code='A'+(x*17+y*101)%21; // Reduce the urge to cheat
568 Mine* mine = (Mine*)item( y, x );
569 int st = (int)mine->state(); if ( mine->isMined() ) st+=5;
570 grid += code + st;
571 }
572 }
573 cfg.writeEntry("Grid",grid);
574}
575
576void MineField::readConfig(Config& cfg)
577{
578 cfg.setGroup("Field");
579 lev = cfg.readNumEntry("Level",1);
580 setup(lev);
581 flagAction = NoAction;
582 ignoreClick = FALSE;
583 currRow = currCol = 0;
584 QString grid = cfg.readEntry("Grid");
585 if ( !grid.isEmpty() ) {
586 int i=0;
587 minecount=0;
588 mineguess=0;
589 for ( int x = 0; x < numCols(); x++ ) {
590 for ( int y = 0; y < numRows(); y++ ) {
591 char code='A'+(x*17+y*101)%21; // Reduce the urge to cheat
592 int st = (char)(QChar)grid[i++]-code;
593 Mine* mine = (Mine*)item( y, x );
594 if ( st >= 5 ) {
595 st-=5;
596 mine->setMined(TRUE);
597 minecount++;
598 mineguess++;
599 }
600 mine->setState((Mine::MineState)st);
601 switch ( mine->state() ) {
602 case Mine::Flagged:
603 if (mine->isMined())
604 minecount--;
605 mineguess--;
606 break;
607 case Mine::Empty:
608 --nonminecount;
609 }
610 }
611 }
612 for ( int x = 0; x < numCols(); x++ ) {
613 for ( int y = 0; y < numRows(); y++ ) {
614 Mine* mine = (Mine*)item( y, x );
615 if ( mine->state() == Mine::Empty )
616 mine->setHint(getHint(y,x));
617 }
618 }
619 }
620 setState( Playing );
621 emit mineCount( mineguess );
622}
623