Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

fifteen.cpp

Go to the documentation of this file.
00001 /**********************************************************************
00002 ** Copyright (C) 2000-2002 Trolltech AS.  All rights reserved.
00003 **
00004 ** This file is part of the Qtopia Environment.
00005 **
00006 ** This file may be distributed and/or modified under the terms of the
00007 ** GNU General Public License version 2 as published by the Free Software
00008 ** Foundation and appearing in the file LICENSE.GPL included in the
00009 ** packaging of this file.
00010 **
00011 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00012 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00013 **
00014 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
00015 **
00016 ** Contact info@trolltech.com if any conditions of this licensing are
00017 ** not clear to you.
00018 **
00019 **********************************************************************/
00020 
00021 #include "fifteen.h"
00022 
00023 #include "fifteenconfigdialog.h"
00024 
00025 #include <opie2/ofileselector.h>
00026 #include <opie2/oresource.h>
00027 
00028 #include <qtopia/config.h>
00029 #include <qtopia/qpeapplication.h>
00030 
00031 #include <qvbox.h>
00032 #include <qaction.h>
00033 #include <qpainter.h>
00034 #include <qmessagebox.h>
00035 #include <qtoolbar.h>
00036 #include <qmenubar.h>
00037 #include <qimage.h>
00038 
00039 #include <stdlib.h>
00040 #include <time.h>
00041 
00042 FifteenMainWindow::FifteenMainWindow(QWidget *parent, const char* name, WFlags fl)
00043   : QMainWindow( parent, name, fl )
00044 {
00045 
00046   // random seed
00047   srand(time(0));
00048   setCaption( tr("Fifteen Pieces") );
00049 
00050   QToolBar *toolbar = new QToolBar(this);
00051   toolbar->setHorizontalStretchable( FALSE );
00052   QMenuBar *menubar = new QMenuBar( toolbar );
00053   menubar->setMargin(0);
00054   QPopupMenu *game = new QPopupMenu( this );
00055   menubar->insertItem( tr( "Game" ), game );
00056 
00057   QWidget *spacer = new QWidget( toolbar );
00058   spacer->setBackgroundMode( PaletteButton );
00059   toolbar->setStretchableWidget( spacer );
00060 
00061 
00062   setToolBarsMovable( FALSE );
00063   QVBox *vbox = new QVBox( this );
00064   PiecesTable *table = new PiecesTable( vbox );
00065   setCentralWidget(vbox);
00066 
00067 
00068 
00069   QAction *a = new QAction( tr( "Randomize" ), Opie::Core::OResource::loadPixmap( "new", Opie::Core::OResource::SmallIcon ),
00070                             QString::null, 0, this, 0 );
00071   connect( a, SIGNAL( activated() ), table, SLOT( slotRandomize() ) );
00072   a->addTo( game );
00073   a->addTo( toolbar );
00074 
00075 
00076   a  = new QAction( tr("Configure"), Opie::Core::OResource::loadPixmap( "SettingsIcon", Opie::Core::OResource::SmallIcon ),
00077                     QString::null, 0, this, 0 );
00078   connect( a, SIGNAL( activated()), table, SLOT( slotConfigure()) );
00079   a->addTo( game );
00080 }
00081 
00082 
00083 
00084 
00088 PiecesTable::PiecesTable(QWidget* parent, const char* name )
00089   : QTableView(parent, name), _menu(0), _randomized(false),
00090     _dialog( 0l )
00091 {
00092   // setup table view
00093   setFrameStyle(StyledPanel | Sunken);
00094   setBackgroundMode(NoBackground);
00095   setMouseTracking(true);
00096 
00097   setNumRows(4);
00098   setNumCols(4);
00099 
00100   // init arrays
00101   readConfig();
00102   initColors();
00103 
00104 }
00105 
00106 
00107 PiecesTable::~PiecesTable()
00108 {
00109   writeConfig();
00110   clear();
00111 }
00112 
00113 void PiecesTable::writeConfig()
00114 {
00115   Config cfg("Fifteen");
00116   cfg.setGroup("Game");
00117   QStringList map;
00118 
00119   int items = numRows()*numCols();
00120 
00121   for (int i = 0; i < items; i++)
00122     map.append( QString::number( _map[i] ) );
00123 
00124   cfg.writeEntry("Map", map, '-');
00125   cfg.writeEntry("Randomized", _randomized );
00126   cfg.writeEntry("Image", _image );
00127   cfg.writeEntry("Rows", numRows() );
00128   cfg.writeEntry("Cols", numCols() );
00129 }
00130 
00131 void PiecesTable::readConfig()
00132 {
00133   Config cfg("Fifteen");
00134   cfg.setGroup("Game");
00135   QStringList map = cfg.readListEntry("Map", '-');
00136   _randomized = cfg.readBoolEntry( "Randomized", FALSE );
00137   _image = cfg.readEntry( "Image", QString::null );
00138 
00139   int rows = cfg.readNumEntry( "Rows", 4 );
00140   int cols = cfg.readNumEntry( "Cols", 4 );
00141   uint items= rows*cols;
00142   setNumRows( rows );
00143   setNumCols( cols );
00144 
00145   initMap();
00146 
00147   /* if we've more items than 'stones' don't restore the state */
00148   if ( items > map.count() )
00149       return;
00150 
00151 
00152   uint i = 0;
00153   for ( QStringList::Iterator it = map.begin(); it != map.end(); ++it ) {
00154     _map[i] = (*it).toInt();
00155     i++;
00156     if ( i > items ) break;
00157   }
00158 
00159 }
00160 
00161 
00162 void PiecesTable::clear() {
00163     /* clean up and resize */
00164     for (uint i = 0; i < _pixmap.count(); ++i )
00165         delete _pixmap[i];
00166     _pixmap.resize( numRows()*numCols() );
00167 }
00168 
00169 /*
00170  * Let us pre-render the tiles. Either we've a Custom Image as
00171  * background or we use the drawRect  to fill the background and
00172  * last we put the number on it
00173  */
00174 void PiecesTable::slotCustomImage( const QString& _str ) {
00175     QString str = _str;
00176 
00177 
00178     /* couldn't load image fall back to plain tiles*/
00179     QImage img = QImage(str);
00180     QPixmap pix;
00181     if(img.isNull())
00182         str = QString::null;
00183     else{
00184         img = img.smoothScale( width(),height() );
00185         pix.convertFromImage( img );
00186     }
00187 
00188     /* initialize base point */
00189     uint image=0;
00190 
00191     /* clear the old tiles */
00192     clear();
00193 
00194     /* used variables */
00195     int cols = numCols();
00196     int rows = numRows();
00197     int cellW   = cellWidth();
00198     int cellH   = cellHeight();
00199     int x2      = cellW-1;
00200     int y2      = cellH-1;
00201     bool empty  = str.isEmpty();
00202     double bw      = empty ? 0.9 : 0.98;
00203     int x_offset = cellW - int(cellW * bw);     // 10% should be enough
00204     int y_offset = cellH - int(cellH * bw);
00205 
00206     /* border polygon calculation*/
00207     initPolygon(cellW, cellH, x_offset, y_offset );
00208 
00209     /* avoid crashes with isNull() pixmap later */
00210     if ( cellW == 0 || cellH == 0 ) {
00211         _pixmap.resize( 0 );
00212         return;
00213     }
00214 
00215     /* make it bold and bigger */
00216     QFont f = font();
00217     f.setPixelSize(18);
00218     f.setBold( TRUE );
00219 
00220     /* for every tile */
00221     for(int row = 0; row < rows; ++row ) {
00222         for(int col= 0; col < cols; ++col) {
00223             QPixmap *pip = new QPixmap(cellW, cellH );
00224             QPainter *p = new QPainter(pip );
00225             p->setFont( f );
00226 
00227             /* draw the tradional tile  or a part of the pixmap*/
00228             if(empty) {
00229                 p->setBrush(_colors[image]);
00230                 p->setPen(NoPen);
00231                 p->drawRect(0,0,cellW,cellH);
00232             }else
00233                 p->drawPixmap(0, 0, pix,col*cellW, row*cellH, cellW, cellH );
00234 
00235             // draw borders
00236             if (height() > 40) {
00237                 p->setBrush(_colors[image].light(130));
00238                 p->drawPolygon(light_border);
00239 
00240                 p->setBrush(_colors[image].dark(130));
00241                 p->drawPolygon(dark_border);
00242             }
00243 
00244             // draw number
00245             p->setPen(black);
00246             p->drawText(0, 0, x2, y2, AlignHCenter | AlignVCenter, QString::number(image+1));
00247 
00248             delete p;
00249             _pixmap[image++] =  pip;
00250         }
00251     }
00252     _image = str;
00253 }
00254 
00255 /*
00256  * Calculate 3d-effect borders
00257  */
00258 void PiecesTable::initPolygon(int cell_w, int cell_h, int x_offset, int y_offset ) {
00259     light_border.setPoints(6,
00260                            0, 0,
00261                            cell_w, 0,
00262                            cell_w - x_offset, y_offset,
00263                            x_offset, y_offset,
00264                            x_offset, cell_h - y_offset,
00265                            0, cell_h);
00266 
00267     dark_border.setPoints(6,
00268                           cell_w, 0,
00269                           cell_w, cell_h,
00270                           0, cell_h,
00271                           x_offset, cell_h - y_offset,
00272                           cell_w - x_offset, cell_h - y_offset,
00273                           cell_w - x_offset, y_offset);
00274 }
00275 
00276 void PiecesTable::paintCell(QPainter *p, int row, int col)
00277 {
00278   int w = cellWidth();
00279   int h = cellHeight();
00280 
00281   uint pos = col+row*numCols();
00282 
00283   /* sanity check. setNumRows()/setNumCols() calls repaint() directly */
00284   if ( pos >= _map.count() ) {
00285       p->drawRect(0, 0, w, h);
00286       return;
00287   }
00288 
00289   int number = _map[col + row * numCols()] + 1;
00290 
00291   // draw cell background
00292   if(number == numCols()*numRows() ) {
00293     p->setBrush(colorGroup().background());
00294     p->setPen(NoPen);
00295     p->drawRect(0, 0, w, h);
00296     return;
00297   }
00298 
00299   /* no tiles then contentRect() is not visible or too small anyway */
00300   if( _pixmap.count() == 0 )
00301       return;
00302 
00303   p->drawPixmap(0, 0, *(_pixmap[(number-1 )]) );
00304 }
00305 
00306 void PiecesTable::resizeEvent(QResizeEvent *e)
00307 {
00308   /*
00309    * null if we faked it after the config dialog ran to
00310    * regenerate everything
00311    */
00312   if ( e )
00313       QTableView::resizeEvent(e);
00314 
00315   setCellWidth(contentsRect().width()/ numCols());
00316   setCellHeight(contentsRect().height() / numRows());
00317 
00318 
00319   /* update the image and calculate border*/
00320   slotCustomImage( _image );
00321 
00322 }
00323 
00324 void PiecesTable::initColors()
00325 {
00326   _colors.resize(numRows() * numCols());
00327   for (int r = 0; r < numRows(); r++)
00328     for (int c = 0; c < numCols(); c++)
00329       _colors[c + r *numCols()] = QColor( 255 - (70 * c)%255 ,255 - (70 * r)%255, 150);
00330 }
00331 
00332 void PiecesTable::initMap()
00333 {
00334   int items = numCols()*numRows();
00335   _map.resize( items );
00336   for ( int i = 0; i < items; i++)
00337     _map[i] = i;
00338 
00339   _randomized = false;
00340 }
00341 
00342 void PiecesTable::randomizeMap()
00343 {
00344   initMap();
00345   _randomized = true;
00346   // find the free position
00347   int cols = numCols();
00348   int rows = numRows();
00349   int pos = _map.find( cols*rows -1 );
00350 
00351   int move = 0;
00352   while ( move < 333 ) {
00353 
00354     int frow = pos / cols;
00355     int fcol = pos - frow * cols;
00356 
00357     // find click position
00358     int row = rand()%rows;
00359     int col = rand()%cols;
00360 
00361     // sanity check
00362     if ( row < 0 || row >= rows ) continue;
00363     if ( col < 0 || col >= cols ) continue;
00364     if ( row != frow && col != fcol ) continue;
00365 
00366     move++;
00367 
00368     // rows match -> shift pieces
00369     if(row == frow) {
00370 
00371       if (col < fcol) {
00372         for(int c = fcol; c > col; c--) {
00373           _map[c + row * cols] = _map[ c-1 + row *cols];
00374         }
00375       }
00376       else if (col > fcol) {
00377         for(int c = fcol; c < col; c++) {
00378           _map[c + row * cols] = _map[ c+1 + row *cols];
00379         }
00380       }
00381     }
00382     // cols match -> shift pieces
00383     else if (col == fcol) {
00384 
00385       if (row < frow) {
00386         for(int r = frow; r > row; r--) {
00387           _map[col + r * cols] = _map[ col + (r-1) *cols];
00388         }
00389       }
00390       else if (row > frow) {
00391         for(int r = frow; r < row; r++) {
00392           _map[col + r * cols] = _map[ col + (r+1) *cols];
00393         }
00394       }
00395     }
00396     // move free cell to click position
00397     _map[pos=(col + row * cols)] = rows*cols-1;
00398   }
00399   repaint();
00400 }
00401 
00402 void PiecesTable::checkwin()
00403 {
00404   if(!_randomized) return;
00405 
00406   int items=numCols()*numRows();
00407   int i;
00408   for (i = 0; i < items; i++)
00409     if(i != _map[i])
00410       break;
00411 
00412   if (i == items) {
00413     QMessageBox::information(this, tr("Fifteen Pieces"),
00414                              tr("Congratulations!\nYou win the game!"));
00415     _randomized = FALSE;
00416   }
00417 
00418 }
00419 
00420 void PiecesTable::slotRandomize()
00421 {
00422   randomizeMap();
00423 }
00424 
00425 void PiecesTable::slotReset()
00426 {
00427   initMap();
00428   repaint();
00429 }
00430 
00431 void PiecesTable::mousePressEvent(QMouseEvent* e)
00432 {
00433   QTableView::mousePressEvent(e);
00434 
00435   if (e->button() == RightButton) {
00436 
00437     // setup RMB pupup menu
00438     if(!_menu) {
00439       _menu = new QPopupMenu(this);
00440       _menu->insertItem(tr("R&andomize Pieces"), mRandomize);
00441       _menu->insertItem(tr("&Reset Pieces"), mReset);
00442       _menu->adjustSize();
00443     }
00444 
00445     // execute RMB popup and check result
00446     switch(_menu->exec(mapToGlobal(e->pos()))) {
00447     case mRandomize:
00448       randomizeMap();
00449       break;
00450     case mReset:
00451       initMap();
00452       repaint();
00453       break;
00454     default:
00455       break;
00456     }
00457   }
00458   else {
00459     // GAME LOGIC
00460     int cols = numCols();
00461     int rows = numRows();
00462     int item = cols*rows -1;
00463 
00464     // find the free position
00465     int pos = _map.find(item);
00466     if(pos < 0) return;
00467 
00468     int frow = pos / cols;
00469     int fcol = pos - frow * cols;
00470 
00471     // find click position
00472     int row = findRow(e->y());
00473     int col = findCol(e->x());
00474 
00475     // sanity check
00476     if (row < 0 || row >= rows) return;
00477     if (col < 0 || col >= cols) return;
00478     if ( row != frow && col != fcol ) return;
00479 
00480     // valid move?
00481     if(row != frow && col != fcol) return;
00482 
00483     // rows match -> shift pieces
00484     if(row == frow) {
00485 
00486       if (col < fcol) {
00487         for(int c = fcol; c > col; c--) {
00488           _map[c + row * cols] = _map[ c-1 + row *cols];
00489           updateCell(row, c, false);
00490         }
00491       }
00492       else if (col > fcol) {
00493         for(int c = fcol; c < col; c++) {
00494           _map[c + row * cols] = _map[ c+1 + row *cols];
00495           updateCell(row, c, false);
00496         }
00497       }
00498     }
00499     // cols match -> shift pieces
00500     else if (col == fcol) {
00501 
00502       if (row < frow) {
00503         for(int r = frow; r > row; r--) {
00504           _map[col + r * cols] = _map[ col + (r-1) *cols];
00505           updateCell(r, col, false);
00506         }
00507       }
00508       else if (row > frow) {
00509         for(int r = frow; r < row; r++) {
00510           _map[col + r * cols] = _map[ col + (r+1) *cols];
00511           updateCell(r, col, false);
00512         }
00513       }
00514     }
00515     // move free cell to click position
00516     _map[col + row * cols] = item;
00517     updateCell(row, col, false);
00518 
00519     // check if the player wins with this move
00520     checkwin();
00521   }
00522 }
00523 
00524 void PiecesTable::slotConfigure() {
00525     if ( !_dialog )
00526         _dialog = new FifteenConfigDialog(this, "Fifteen Configure Dialog", true );
00527 
00528 
00529     _dialog->setImageSrc( _image );
00530     _dialog->setGameboard( numRows(), numCols() );
00531 
00532     if ( QPEApplication::execDialog(_dialog) == QDialog::Accepted ) {
00533         /*
00534          * update the board grid and reinit the game if changed
00535          * First set new columns so the update will regenerate the
00536          * tiles with slotCustomImage
00537          */
00538         _image = _dialog->imageSrc();
00539         if (numRows() != _dialog->rows() ||
00540             numCols() != _dialog->columns() ) {
00541             setNumCols(_dialog->columns());
00542             setNumRows(_dialog->rows());
00543             slotReset();
00544         }
00545         resizeEvent( 0l );
00546 
00547 
00548         update();
00549     }
00550 }

Generated on Sat Nov 5 16:17:19 2005 for OPIE by  doxygen 1.4.2