00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "minefield.h"
00021
00022 #include <qtopia/config.h>
00023 #include <qtopia/qpeapplication.h>
00024
00025 #include <qtimer.h>
00026
00027 #include <stdlib.h>
00028
00029 static const char *pix_flag[]={
00030 "13 13 3 1",
00031 "# c #000000",
00032 "x c #ff0000",
00033 ". c None",
00034 ".............",
00035 ".............",
00036 ".....#xxxxxx.",
00037 ".....#xxxxxx.",
00038 ".....#xxxxxx.",
00039 ".....#xxxxxx.",
00040 ".....#.......",
00041 ".....#.......",
00042 ".....#.......",
00043 ".....#.......",
00044 "...#####.....",
00045 "..#######....",
00046 "............."};
00047
00048 static const char *pix_mine[]={
00049 "13 13 3 1",
00050 "# c #000000",
00051 ". c None",
00052 "a c #ffffff",
00053 "......#......",
00054 "......#......",
00055 "..#.#####.#..",
00056 "...#######...",
00057 "..##aa#####..",
00058 "..##aa#####..",
00059 "#############",
00060 "..#########..",
00061 "..#########..",
00062 "...#######...",
00063 "..#.#####.#..",
00064 "......#......",
00065 "......#......"};
00066
00067
00068 static const int maxGrid = 28;
00069 static const int minGrid = 12;
00070
00071
00072
00073 class Mine : public Qt
00074 {
00075 public:
00076 enum MineState {
00077 Hidden = 0,
00078 Empty,
00079 Mined,
00080 Flagged,
00081 #ifdef MARK_UNSURE
00082 Unsure,
00083 #endif
00084 Exploded,
00085 Wrong
00086 };
00087
00088 Mine( MineField* );
00089 void paint( QPainter * p, const QColorGroup & cg, const QRect & cr );
00090
00091 QSize sizeHint() const { return QSize( maxGrid, maxGrid ); }
00092
00093 void activate( bool sure = TRUE );
00094 void setHint( int );
00095
00096 void setState( MineState );
00097 MineState state() const { return st; }
00098
00099 bool isMined() const { return mined; }
00100 void setMined( bool m ) { mined = m; }
00101
00102 static void paletteChange();
00103
00104 private:
00105 bool mined;
00106 int hint;
00107
00108 MineState st;
00109 MineField *field;
00110
00111 static QPixmap* knownField;
00112 static QPixmap* unknownField;
00113 static QPixmap* flag_pix;
00114 static QPixmap* mine_pix;
00115 };
00116
00117 QPixmap* Mine::knownField = 0;
00118 QPixmap* Mine::unknownField = 0;
00119 QPixmap* Mine::flag_pix = 0;
00120 QPixmap* Mine::mine_pix = 0;
00121
00122 Mine::Mine( MineField *f )
00123 {
00124 mined = FALSE;
00125 st = Hidden;
00126 hint = 0;
00127 field = f;
00128 }
00129
00130 void Mine::activate( bool sure )
00131 {
00132 if ( !sure ) {
00133 switch ( st ) {
00134 case Hidden:
00135 setState( Flagged );
00136 break;
00137 case Flagged:
00138 #ifdef MARK_UNSURE
00139 setState( Unsure );
00140 break;
00141 case Unsure:
00142 #endif
00143 setState( Hidden );
00144 default:
00145 break;
00146 }
00147 } else if ( st == Flagged ) {
00148 return;
00149 } else {
00150 if ( mined ) {
00151 setState( Exploded );
00152 } else {
00153 setState( Empty );
00154 }
00155 }
00156 }
00157
00158 void Mine::setState( MineState s )
00159 {
00160 st = s;
00161 }
00162
00163 void Mine::setHint( int h )
00164 {
00165 hint = h;
00166 }
00167
00168 void Mine::paletteChange()
00169 {
00170 delete knownField;
00171 knownField = 0;
00172 delete unknownField;
00173 unknownField = 0;
00174 delete mine_pix;
00175 mine_pix = 0;
00176 delete flag_pix;
00177 flag_pix = 0;
00178 }
00179
00180 void Mine::paint( QPainter* p, const QColorGroup &cg, const QRect& cr )
00181 {
00182 int x = cr.x();
00183 int y = cr.y();
00184 if ( !knownField || knownField->width() != cr.width() ||
00185 knownField->height() != cr.height() ) {
00186 delete knownField;
00187 knownField = new QPixmap( cr.width(), cr.height() );
00188 QPainter pp( knownField );
00189 QBrush br( cg.button().dark(115) );
00190 qDrawWinButton( &pp, QRect( 0, 0, cr.width(), cr.height() ), cg, TRUE, &br );
00191 }
00192
00193 const int pmmarg=cr.width()/5;
00194
00195 if ( !unknownField || unknownField->width() != cr.width() ||
00196 unknownField->height() != cr.height() ) {
00197 delete unknownField;
00198 unknownField = new QPixmap( cr.width(), cr.height() );
00199 QPainter pp( unknownField );
00200 QBrush br( cg.button() );
00201 qDrawWinButton( &pp, QRect( 0, 0, cr.width(), cr.height() ), cg, FALSE, &br );
00202 }
00203
00204 if ( !flag_pix || flag_pix->width() != cr.width()-pmmarg*2 ||
00205 flag_pix->height() != cr.height()-pmmarg*2 ) {
00206 delete flag_pix;
00207 flag_pix = new QPixmap( cr.width()-pmmarg*2, cr.height()-pmmarg*2 );
00208 flag_pix->convertFromImage( QImage(pix_flag).smoothScale(cr.width()-pmmarg*2, cr.height()-pmmarg*2) );
00209 }
00210
00211 if ( !mine_pix || mine_pix->width() != cr.width()-pmmarg*2 ||
00212 mine_pix->height() != cr.height()-pmmarg*2 ) {
00213 delete mine_pix;
00214 mine_pix = new QPixmap( cr.width()-pmmarg*2, cr.height()-pmmarg*2 );
00215 mine_pix->convertFromImage( QImage(pix_mine).smoothScale(cr.width()-pmmarg*2, cr.height()-pmmarg*2) );
00216 }
00217
00218 p->save();
00219
00220 switch(st) {
00221 case Hidden:
00222 p->drawPixmap( x, y, *unknownField );
00223 break;
00224 case Empty:
00225 p->drawPixmap( x, y, *knownField );
00226 if ( hint > 0 ) {
00227 switch( hint ) {
00228 case 1:
00229 p->setPen( blue );
00230 break;
00231 case 2:
00232 p->setPen( green.dark() );
00233 break;
00234 case 3:
00235 p->setPen( red );
00236 break;
00237 case 4:
00238 p->setPen( darkYellow.dark() );
00239 break;
00240 case 5:
00241 p->setPen( darkMagenta );
00242 break;
00243 case 6:
00244 p->setPen( darkRed );
00245 break;
00246 default:
00247 p->setPen( black );
00248 break;
00249 }
00250 p->drawText( cr, AlignHCenter | AlignVCenter, QString::number( hint ) );
00251 }
00252 break;
00253 case Mined:
00254 p->drawPixmap( x, y, *knownField );
00255 p->drawPixmap( x+pmmarg, y+pmmarg, *mine_pix );
00256 break;
00257 case Exploded:
00258 p->drawPixmap( x, y, *knownField );
00259 p->drawPixmap( x+pmmarg, y+pmmarg, *mine_pix );
00260 p->setPen( red );
00261 p->drawText( cr, AlignHCenter | AlignVCenter, "X" );
00262 break;
00263 case Flagged:
00264 p->drawPixmap( x, y, *unknownField );
00265 p->drawPixmap( x+pmmarg, y+pmmarg, *flag_pix );
00266 break;
00267 #ifdef MARK_UNSURE
00268 case Unsure:
00269 p->drawPixmap( x, y, *unknownField );
00270 p->drawText( cr, AlignHCenter | AlignVCenter, "?" );
00271 break;
00272 #endif
00273 case Wrong:
00274 p->drawPixmap( x, y, *unknownField );
00275 p->drawPixmap( x+pmmarg, y+pmmarg, *flag_pix );
00276 p->setPen( red );
00277 p->drawText( cr, AlignHCenter | AlignVCenter, "X" );
00278 break;
00279 }
00280
00281 p->restore();
00282 }
00283
00284
00285
00286
00287
00288 MineField::MineField( QWidget* parent, const char* name )
00289 : QScrollView( parent, name )
00290 {
00291 viewport()->setBackgroundMode( NoBackground );
00292 setState( GameOver );
00293
00294 setSizePolicy( QSizePolicy( QSizePolicy::Maximum, QSizePolicy::Maximum ) );
00295
00296 setFocusPolicy( QWidget::NoFocus );
00297
00298 holdTimer = new QTimer( this );
00299 connect( holdTimer, SIGNAL( timeout() ), this, SLOT( held() ) );
00300
00301 flagAction = NoAction;
00302 ignoreClick = FALSE;
00303 currRow = currCol = -1;
00304 minecount=0;
00305 mineguess=0;
00306 nonminecount=0;
00307 cellSize = -1;
00308
00309 numRows = numCols = 0;
00310 mines = NULL;
00311 }
00312
00313 MineField::~MineField()
00314 {
00315 for ( int i = 0; i < numCols*numRows; i++ )
00316 delete mines[i];
00317 delete[] mines;
00318 }
00319
00320 void MineField::setState( State st )
00321 {
00322 stat = st;
00323 }
00324
00325 void MineField::setup( int level )
00326 {
00327 lev = level;
00328 setState( Waiting );
00329
00330
00331 int i;
00332 for ( i = 0; i < numCols*numRows; i++ )
00333 delete mines[i];
00334 delete[] mines;
00335
00336 switch( lev ) {
00337 case 1:
00338 numRows = 9 ;
00339 numCols = 9 ;
00340 minecount = 12;
00341 break;
00342 case 2:
00343 numRows = 13;
00344 numCols = 13;
00345 minecount = 33;
00346 break;
00347 case 3:
00348 numCols = 18;
00349 numRows = 18;
00350 minecount = 66 ;
00351 break;
00352 }
00353 mines = new Mine* [numRows*numCols];
00354 for ( i = 0; i < numCols*numRows; i++ )
00355 mines[i] = new Mine( this );
00356
00357
00358 nonminecount = numRows*numCols - minecount;
00359 mineguess = minecount;
00360 emit mineCount( mineguess );
00361 Mine::paletteChange();
00362
00363 if ( availableRect.isValid() )
00364 setCellSize(findCellSize());
00365
00366
00367 updateContents( 0, 0, numCols*cellSize, numRows*cellSize );
00368 updateGeometry();
00369 }
00370
00371 void MineField::drawContents( QPainter * p, int clipx, int clipy, int clipw, int cliph )
00372 {
00373 int c1 = clipx / cellSize;
00374 int c2 = ( clipx + clipw - 1 ) / cellSize;
00375 int r1 = clipy / cellSize;
00376 int r2 = ( clipy + cliph - 1 ) / cellSize;
00377
00378 for ( int c = c1; c <= c2 ; c++ ) {
00379 for ( int r = r1; r <= r2 ; r++ ) {
00380 int x = c * cellSize;
00381 int y = r * cellSize;
00382 Mine *m = mine( r, c );
00383 if ( m )
00384 m->paint( p, colorGroup(), QRect(x, y, cellSize, cellSize ) );
00385 }
00386 }
00387 }
00388
00389
00390
00391
00392
00393 void MineField::setAvailableRect( const QRect &r )
00394 {
00395 availableRect = r;
00396 int newCellSize = findCellSize();
00397
00398
00399 if ( newCellSize == cellSize ) {
00400 setCellSize( cellSize );
00401 } else {
00402 viewport()->setUpdatesEnabled( FALSE );
00403 setCellSize( newCellSize );
00404 viewport()->setUpdatesEnabled( TRUE );
00405 viewport()->repaint( TRUE );
00406 }
00407 }
00408
00409 int MineField::findCellSize()
00410 {
00411 int w = availableRect.width() - 2;
00412 int h = availableRect.height() - 2;
00413 int cellsize;
00414
00415 cellsize = QMIN( w/numCols, h/numRows );
00416 cellsize = QMIN( QMAX( cellsize, minGrid ), maxGrid );
00417 return cellsize;
00418 }
00419
00420
00421 void MineField::setCellSize( int cellsize )
00422 {
00423 int b = 2;
00424
00425 int w2 = cellsize*numCols;
00426 int h2 = cellsize*numRows;
00427
00428 int w = QMIN( availableRect.width(), w2+b );
00429 int h = QMIN( availableRect.height(), h2+b );
00430
00431
00432
00433
00434
00435
00436 resizeContents(w2, h2);
00437
00438 if ( availableRect.height() < h2 &&
00439 availableRect.width() - w > style().scrollBarExtent().width() ) {
00440 w += style().scrollBarExtent().width();
00441 }
00442
00443 setGeometry( availableRect.x() + (availableRect.width()-w)/2,
00444 availableRect.y() + (availableRect.height()-h)/2, w, h );
00445 cellSize = cellsize;
00446 }
00447
00448
00449 void MineField::placeMines()
00450 {
00451 int mines = minecount;
00452 while ( mines ) {
00453 int col = int((double(rand()) / double(RAND_MAX)) * numCols);
00454 int row = int((double(rand()) / double(RAND_MAX)) * numRows);
00455
00456 Mine* m = mine( row, col );
00457
00458 if ( m && !m->isMined() && m->state() == Mine::Hidden ) {
00459 m->setMined( TRUE );
00460 mines--;
00461 }
00462 }
00463 }
00464
00465
00466 void MineField::updateCell( int r, int c )
00467 {
00468 updateContents( c*cellSize, r*cellSize, cellSize, cellSize );
00469 }
00470
00471
00472 void MineField::contentsMousePressEvent( QMouseEvent* e )
00473 {
00474 int c = e->pos().x() / cellSize;
00475 int r = e->pos().y() / cellSize;
00476 if ( onBoard( r, c ) )
00477 cellPressed( r, c );
00478 else
00479 currCol = currRow = -1;
00480 }
00481
00482 void MineField::contentsMouseReleaseEvent( QMouseEvent* e )
00483 {
00484 int c = e->pos().x() / cellSize;
00485 int r = e->pos().y() / cellSize;
00486 if ( onBoard( r, c ) && c == currCol && r == currRow )
00487 cellClicked( r, c );
00488
00489
00490 if ( flagAction == FlagNext ) {
00491 flagAction = NoAction;
00492 }
00493 }
00494
00495
00496
00497
00498
00499
00500
00501
00502 void MineField::cellPressed( int row, int col )
00503 {
00504 if ( state() == GameOver )
00505 return;
00506 currRow = row;
00507 currCol = col;
00508 if ( state() == Playing )
00509 holdTimer->start( 150, TRUE );
00510 }
00511
00512 void MineField::held()
00513 {
00514 flagAction = FlagNext;
00515 updateMine( currRow, currCol );
00516 ignoreClick = TRUE;
00517 }
00518
00519
00520
00521
00522 void MineField::keyPressEvent( QKeyEvent* e )
00523 {
00524 #if defined(Q_WS_QWS) || defined(_WS_QWS_)
00525 flagAction = ( e->key() == Key_Up ) ? FlagOn : NoAction;
00526 #else
00527 flagAction = ( ( e->state() & ShiftButton ) == ShiftButton ) ? FlagOn : NoAction;
00528 #endif
00529 }
00530
00531 void MineField::keyReleaseEvent( QKeyEvent* )
00532 {
00533 flagAction = NoAction;
00534 }
00535
00536 int MineField::getHint( int row, int col )
00537 {
00538 int hint = 0;
00539 for ( int c = col-1; c <= col+1; c++ )
00540 for ( int r = row-1; r <= row+1; r++ ) {
00541 Mine* m = mine( r, c );
00542 if ( m && m->isMined() )
00543 hint++;
00544 }
00545
00546 return hint;
00547 }
00548
00549 void MineField::setHint( int row, int col )
00550 {
00551 Mine *m = mine( row, col );
00552 if ( !m )
00553 return;
00554
00555 int hint = getHint( row, col );
00556
00557 if ( !hint ) {
00558 for ( int c = col-1; c <= col+1; c++ )
00559 for ( int r = row-1; r <= row+1; r++ ) {
00560 Mine* m = mine( r, c );
00561 if ( m && m->state() == Mine::Hidden ) {
00562 m->activate( TRUE );
00563 nonminecount--;
00564 setHint( r, c );
00565 updateCell( r, c );
00566 }
00567 }
00568 }
00569
00570 m->setHint( hint );
00571 updateCell( row, col );
00572 }
00573
00574
00575
00576
00577
00578
00579 void MineField::cellClicked( int row, int col )
00580 {
00581 if ( state() == GameOver )
00582 return;
00583 if ( state() == Waiting ) {
00584 Mine* m = mine( row, col );
00585 if ( !m )
00586 return;
00587 m->setState( Mine::Empty );
00588 nonminecount--;
00589 placeMines();
00590 setState( Playing );
00591 emit gameStarted();
00592 updateMine( row, col );
00593 } else {
00594 holdTimer->stop();
00595 if ( ignoreClick )
00596 ignoreClick = FALSE;
00597 else
00598 updateMine( row, col );
00599 }
00600 }
00601
00602 void MineField::updateMine( int row, int col )
00603 {
00604 Mine* m = mine( row, col );
00605 if ( !m )
00606 return;
00607
00608 bool wasFlagged = m->state() == Mine::Flagged;
00609 bool wasEmpty = m->state() == Mine::Empty;
00610
00611 m->activate( flagAction == NoAction );
00612
00613 if ( m->state() == Mine::Exploded ) {
00614 emit gameOver( FALSE );
00615 setState( GameOver );
00616 return;
00617 } else if ( m->state() == Mine::Empty ) {
00618 setHint( row, col );
00619 if ( !wasEmpty )
00620 nonminecount--;
00621 }
00622
00623 if ( flagAction != NoAction ) {
00624 if ( m->state() == Mine::Flagged ) {
00625 if (mineguess > 0) {
00626 --mineguess;
00627 emit mineCount( mineguess );
00628 if ( m->isMined() )
00629 --minecount;
00630 } else {
00631 m->setState(Mine::Hidden);
00632 }
00633 } else if ( wasFlagged ) {
00634 ++mineguess;
00635 emit mineCount( mineguess );
00636 if ( m->isMined() )
00637 ++minecount;
00638 }
00639 }
00640
00641 updateCell( row, col );
00642
00643 if ( !minecount && !mineguess || !nonminecount ) {
00644 emit gameOver( TRUE );
00645 setState( GameOver );
00646 }
00647 }
00648
00649 void MineField::showMines()
00650 {
00651 for ( int c = 0; c < numCols; c++ )
00652 for ( int r = 0; r < numRows; r++ ) {
00653 Mine* m = mine( r, c );
00654 if ( !m )
00655 continue;
00656 if ( m->isMined() && m->state() == Mine::Hidden )
00657 m->setState( Mine::Mined );
00658 if ( !m->isMined() && m->state() == Mine::Flagged )
00659 m->setState( Mine::Wrong );
00660
00661 updateCell( r, c );
00662 }
00663 }
00664
00665 void MineField::paletteChange( const QPalette &o )
00666 {
00667 Mine::paletteChange();
00668 QScrollView::paletteChange( o );
00669 }
00670
00671 void MineField::writeConfig(Config& cfg) const
00672 {
00673 cfg.setGroup("Field");
00674 cfg.writeEntry("Level",lev);
00675 QString grid="";
00676 if ( stat == Playing ) {
00677 for ( int x = 0; x < numCols; x++ )
00678 for ( int y = 0; y < numRows; y++ ) {
00679 char code='A'+(x*17+y*101)%21;
00680 const Mine* m = mine( y, x );
00681 int st = (int)m->state(); if ( m->isMined() ) st+=5;
00682 grid += code + st;
00683 }
00684 }
00685 cfg.writeEntry("Grid",grid);
00686 }
00687
00688 void MineField::readConfig(Config& cfg)
00689 {
00690 cfg.setGroup("Field");
00691 lev = cfg.readNumEntry("Level",1);
00692 setup(lev);
00693 flagAction = NoAction;
00694 ignoreClick = FALSE;
00695 currRow = currCol = 0;
00696 QString grid = cfg.readEntry("Grid");
00697 int x;
00698 if ( !grid.isEmpty() ) {
00699 int i=0;
00700 minecount=0;
00701 mineguess=0;
00702 for ( x = 0; x < numCols; x++ ) {
00703 for ( int y = 0; y < numRows; y++ ) {
00704 char code='A'+(x*17+y*101)%21;
00705 int st = (char)(QChar)grid[i++]-code;
00706 Mine* m = mine( y, x );
00707 if ( st >= 5 ) {
00708 st-=5;
00709 m->setMined(TRUE);
00710 minecount++;
00711 mineguess++;
00712 }
00713 m->setState((Mine::MineState)st);
00714 switch ( m->state() ) {
00715 case Mine::Flagged:
00716 if (m->isMined())
00717 minecount--;
00718 mineguess--;
00719 break;
00720 case Mine::Empty:
00721 --nonminecount;
00722 break;
00723 default:
00724 break;
00725 }
00726 }
00727 }
00728 for ( x = 0; x < numCols; x++ ) {
00729 for ( int y = 0; y < numRows; y++ ) {
00730 Mine* m = mine( y, x );
00731 if ( m->state() == Mine::Empty )
00732 m->setHint(getHint(y,x));
00733 }
00734 }
00735 }
00736 setState( Playing );
00737 emit mineCount( mineguess );
00738 }
00739
00740 QSize MineField::sizeHint() const
00741 {
00742 if ( qApp->desktop()->width() >= 240 )
00743 return QSize(200,200);
00744 else
00745 return QSize(160, 160);
00746 }
00747