• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

kmahjongg

GameData.cpp

Go to the documentation of this file.
00001 /*
00002     Copyright (C) 2006 Mauricio Piacentini   <mauricio@tabuleiro.com>
00003 
00004     Kmahjongg is free software; you can redistribute it and/or modify
00005     it under the terms of the GNU General Public License as published by
00006     the Free Software Foundation; either version 2 of the License, or
00007     (at your option) any later version.
00008 
00009     This program is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012     GNU General Public License for more details.
00013 
00014     You should have received a copy of the GNU General Public License
00015     along with this program; if not, write to the Free Software
00016     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00017 */
00018 
00019 #include "GameData.h"
00020 #include <QtDebug>
00021 
00022 
00023 GameData::GameData (BoardLayout * boardlayout) {
00024     m_width = boardlayout->m_width;
00025     m_height = boardlayout->m_height;
00026     m_depth = boardlayout->m_depth;
00027     m_maxTiles = (m_width*m_height*m_depth)/4;
00028 
00029     Highlight = QByteArray(m_width*m_height*m_depth, 0);
00030     Mask = QByteArray(m_width*m_height*m_depth, 0);
00031     Board = QByteArray(m_width*m_height*m_depth, 0);
00032     POSITION e; //constructor initializes it to 0
00033     MoveList = QVector<POSITION>(m_maxTiles, e);
00034     tilePositions = QVector<POSITION>(m_maxTiles, e);
00035     PosTable = QVector<POSITION>(m_maxTiles, e);
00036     positionDepends = QVector<DEPENDENCY>(m_maxTiles);
00037 
00038     //Copy board layout over
00039     boardlayout->copyBoardLayout((UCHAR *) getMaskBytes(), MaxTileNum);
00040 }
00041 
00042 GameData::~GameData () {
00043 
00044 }
00045 
00046 void GameData::putTile( short e, short y, short x, UCHAR f ){
00047     setBoardData(e,y,x,f);
00048     setBoardData(e,y+1,x,f);
00049     setBoardData(e,y+1,x+1,f);
00050     setBoardData(e,y,x+1,f);
00051 }
00052 
00053 bool GameData::tilePresent(int z, int y, int x) {
00054     if ((y<0)||(x<0)||(z<0)||(y>m_height-1)||(x>m_width-1)||(z>m_depth-1)) return false;
00055     return(BoardData(z,y,x)!=0 && MaskData(z,y,x) == '1');
00056 }
00057 
00058 bool GameData::partTile(int z, int y, int x) {
00059     return (BoardData(z,y,x) != 0);
00060 }
00061 
00062 UCHAR GameData::MaskData(short z, short y, short x){
00063     if ((y<0)||(x<0)||(z<0)||(y>m_height-1)||(x>m_width-1)||(z>m_depth-1)) return 0;
00064     return Mask.at((z*m_width*m_height)+(y*m_width)+x);
00065 }
00066 
00067 UCHAR GameData::HighlightData(short z, short y, short x){
00068     if ((y<0)||(x<0)||(z<0)||(y>m_height-1)||(x>m_width-1)||(z>m_depth-1)) return 0;
00069     return Highlight.at((z*m_width*m_height)+(y*m_width)+x);
00070 }
00071 
00072 void GameData::setHighlightData(short z, short y, short x, UCHAR value) {
00073     if ((y<0)||(x<0)||(z<0)||(y>m_height-1)||(x>m_width-1)||(z>m_depth-1)) return ;
00074     Highlight[(z*m_width*m_height)+(y*m_width)+x] = value;
00075 }
00076 
00077 UCHAR GameData::BoardData(short z, short y, short x){
00078     if ((y<0)||(x<0)||(z<0)||(y>m_height-1)||(x>m_width-1)||(z>m_depth-1)) return 0;
00079     return Board.at((z*m_width*m_height)+(y*m_width)+x);
00080 }
00081 
00082 void GameData::setBoardData(short z, short y, short x, UCHAR value) {
00083     if ((y<0)||(x<0)||(z<0)||(y>m_height-1)||(x>m_width-1)||(z>m_depth-1)) return ;
00084     Board[(z*m_width*m_height)+(y*m_width)+x] = value;
00085 }
00086 
00087 POSITION& GameData::MoveListData(short i) {
00088     if ((i>=MoveList.size())|| (i<0)) {
00089       qDebug() << "Attempt to access GameData::MoveListData with invalid index";
00090       i=0 ;
00091     }
00092     return MoveList[i];
00093 }
00094 
00095 void GameData::setMoveListData(short i, POSITION& value){
00096     if ((i>=MoveList.size()) || (i<0)) return ;
00097     MoveList[i] = value;
00098 }
00099 
00100 //Game generation
00101 
00102 // ---------------------------------------------------------
00103 // Generate the position data for the layout from contents of Game.Map.
00104 void GameData::generateTilePositions() {
00105 
00106     numTilesToGenerate = 0;
00107 
00108     for (int z=0; z< m_depth; z++) {
00109         for (int y=0; y< m_height; y++) {
00110             for (int x=0; x< m_width; x++) {
00111                 setBoardData(z,y,x,0);
00112                 if (MaskData(z,y,x) == '1') {
00113                     tilePositions[numTilesToGenerate].x = x;
00114                     tilePositions[numTilesToGenerate].y = y;
00115                     tilePositions[numTilesToGenerate].e = z;
00116                     tilePositions[numTilesToGenerate].f = 254;
00117                     numTilesToGenerate++;
00118                 }
00119             }
00120         }
00121     }
00122 }
00123 
00124 // ---------------------------------------------------------
00125 // Generate the dependency data for the layout from the position data.
00126 // Note that the coordinates of each tile in tilePositions are those of
00127 // the upper left quarter of the tile.
00128 void GameData::generatePositionDepends() {
00129 
00130     // For each tile,
00131     for (int i = 0; i < numTilesToGenerate; i++) {
00132 
00133         // Get its basic position data
00134         int x = tilePositions[i].x;
00135         int y = tilePositions[i].y;
00136         int z = tilePositions[i].e;
00137 
00138         // LHS dependencies
00139         positionDepends[i].lhs_dep[0] = tileAt(x-1, y,   z);
00140         positionDepends[i].lhs_dep[1] = tileAt(x-1, y+1, z);
00141 
00142         // Make them unique
00143         if (positionDepends[i].lhs_dep[1] == positionDepends[i].lhs_dep[0]) {
00144             positionDepends[i].lhs_dep[1] = -1;
00145         }
00146 
00147         // RHS dependencies
00148         positionDepends[i].rhs_dep[0] = tileAt(x+2, y,   z);
00149         positionDepends[i].rhs_dep[1] = tileAt(x+2, y+1, z);
00150 
00151         // Make them unique
00152         if (positionDepends[i].rhs_dep[1] == positionDepends[i].rhs_dep[0]) {
00153             positionDepends[i].rhs_dep[1] = -1;
00154         }
00155 
00156         // Turn dependencies
00157         positionDepends[i].turn_dep[0] = tileAt(x,   y,   z+1);
00158         positionDepends[i].turn_dep[1] = tileAt(x+1, y,   z+1);
00159         positionDepends[i].turn_dep[2] = tileAt(x+1, y+1, z+1);
00160         positionDepends[i].turn_dep[3] = tileAt(x,   y+1, z+1);
00161 
00162         // Make them unique
00163         for (int j = 0; j < 3; j++) {
00164             for (int k = j+1; k < 4; k++) {
00165                 if (positionDepends[i].turn_dep[j] ==
00166                     positionDepends[i].turn_dep[k]) {
00167                     positionDepends[i].turn_dep[k] = -1;
00168                 }
00169             }
00170         }
00171 
00172         // Placement dependencies
00173         positionDepends[i].place_dep[0] = tileAt(x,   y,   z-1);
00174         positionDepends[i].place_dep[1] = tileAt(x+1, y,   z-1);
00175         positionDepends[i].place_dep[2] = tileAt(x+1, y+1, z-1);
00176         positionDepends[i].place_dep[3] = tileAt(x,   y+1, z-1);
00177 
00178         // Make them unique
00179         for (int j = 0; j < 3; j++) {
00180             for (int k = j+1; k < 4; k++) {
00181                 if (positionDepends[i].place_dep[j] ==
00182                     positionDepends[i].place_dep[k]) {
00183                     positionDepends[i].place_dep[k] = -1;
00184                 }
00185             }
00186         }
00187 
00188         // Filled and free indicators.
00189         positionDepends[i].filled = false;
00190         positionDepends[i].free   = false;
00191     }
00192 }
00193 
00194 // ---------------------------------------------------------
00195 // x, y, z are the coordinates of a *quarter* tile.  This returns the
00196 // index (in positions) of the tile at those coordinates or -1 if there
00197 // is no tile at those coordinates.  Note that the coordinates of each
00198 // tile in positions are those of the upper left quarter of the tile.
00199 int GameData::tileAt(int x, int y, int z) {
00200 
00201     for (int i = 0; i < numTilesToGenerate; i++) {
00202         if (tilePositions[i].e == z) {
00203             if ((tilePositions[i].x == x   && tilePositions[i].y == y) ||
00204                 (tilePositions[i].x == x-1 && tilePositions[i].y == y) ||
00205                 (tilePositions[i].x == x-1 && tilePositions[i].y == y-1) ||
00206                 (tilePositions[i].x == x   && tilePositions[i].y == y-1)) {
00207 
00208                 return i;
00209             }
00210         }
00211     }
00212     return -1;
00213 }
00214 
00215 
00216 // ---------------------------------------------------------
00217 bool GameData::generateSolvableGame() {
00218 
00219     // Initially we want to mark positions on layer 0 so that we have only
00220     // one free position per apparent horizontal line.
00221     for (int i = 0; i < numTilesToGenerate; i++) {
00222 
00223         // Pick a random tile on layer 0
00224         int position, cnt = 0;
00225         do {
00226             position = (int) random.getLong(numTilesToGenerate);
00227             if (cnt++ > (numTilesToGenerate*numTilesToGenerate)) {
00228                 return false; // bail
00229             }
00230         } while (tilePositions[position].e != 0);
00231 
00232         // If there are no other free positions on the same apparent
00233         // horizontal line, we can mark that position as free.
00234         if (onlyFreeInLine(position)) {
00235             positionDepends[position].free = true;
00236         }
00237     }
00238 
00239     // Check to make sure we really got them all.  Very important for
00240     // this algorithm.
00241     for (int i = 0; i < numTilesToGenerate; i++) {
00242         if (tilePositions[i].e == 0 && onlyFreeInLine(i)) {
00243             positionDepends[i].free = true;
00244         }
00245     }
00246 
00247     // Get ready to place the tiles
00248     int lastPosition = -1;
00249     int position = -1;
00250     int position2 = -1;
00251 
00252     // For each position,
00253     for (int i = 0; i < numTilesToGenerate; i++) {
00254 
00255         // If this is the first tile in a 144 tile set,
00256         if ((i % 144) == 0) {
00257 
00258             // Initialise the faces to allocate. For the classic
00259             // dragon board there are 144 tiles. So we allocate and
00260             // randomise the assignment of 144 tiles. If there are > 144
00261             // tiles we will reallocate and re-randomise as we run out.
00262             // One advantage of this method is that the pairs to assign are
00263             // non-linear. In kmahjongg 0.4, If there were > 144 the same
00264             // allocation series was followed. So 154 = 144 + 10 rods.
00265             // 184 = 144 + 40 rods (20 pairs) which overwhemed the board
00266             // with rods and made deadlock games more likely.
00267             randomiseFaces();
00268         }
00269 
00270         // If this is the first half of a pair, there is no previous
00271         // position for the pair.
00272         if ((i & 1) == 0) {
00273             lastPosition = -1;
00274         }
00275 
00276         // Select a position for the tile, relative to the position of
00277         // the last tile placed.
00278         if ((position = selectPosition(lastPosition)) < 0) {
00279             return false; // bail
00280         }
00281         if (i < numTilesToGenerate-1) {
00282             if ((position2 = selectPosition(lastPosition)) < 0) {
00283                 return false; // bail
00284             }
00285             if (tilePositions[position2].e > tilePositions[position].e) {
00286                 position = position2;  // higher is better
00287             }
00288         }
00289 
00290         // Place the tile.
00291         placeTile(position, tilePair[i % 144]);
00292 
00293         // Remember the position
00294         lastPosition = position;
00295     }
00296 
00297     // The game is solvable.
00298     return true;
00299 }
00300 
00301 // ---------------------------------------------------------
00302 // Determines whether it is ok to mark this position as "free" because
00303 // there are no other positions marked "free" in its apparent horizontal
00304 // line.
00305 bool GameData::onlyFreeInLine(int position) {
00306 
00307     int i, i0, w;
00308     int lin, rin, out;
00309     //static int nextLeft[m_maxTiles];
00310     //static int nextRight[m_maxTiles];
00311     QVector<int> nextLeft = QVector<int>(m_maxTiles, 0);
00312     QVector<int> nextRight = QVector<int>(m_maxTiles, 0);
00313 
00314     /* Check left, starting at position */
00315     lin = 0;
00316     out = 0;
00317     nextLeft[lin++] = position;
00318     do {
00319         w = nextLeft[out++];
00320         if (positionDepends[w].free || positionDepends[w].filled) {
00321             return false;
00322         }
00323         if ((i = positionDepends[w].lhs_dep[0]) != -1) {
00324             nextLeft[lin++] = i;
00325         }
00326         i0 = i;
00327         if ((i = positionDepends[w].lhs_dep[1]) != -1 && i0 != i) {
00328             nextLeft[lin++] = i;
00329         }
00330     }
00331     while (lin > out) ;
00332 
00333     /* Check right, starting at position */
00334     rin = 0;
00335     out = 0;
00336     nextRight[rin++] = position;
00337     do {
00338         w = nextRight[out++];
00339         if (positionDepends[w].free || positionDepends[w].filled) {
00340             return false;
00341         }
00342         if ((i = positionDepends[w].rhs_dep[0]) != -1) {
00343             nextRight[rin++] = i;
00344         }
00345         i0 = i;
00346         if ((i = positionDepends[w].rhs_dep[1]) != -1 && i0 != i) {
00347             nextRight[rin++] = i;
00348         }
00349     }
00350     while (rin > out) ;
00351 
00352     // Here, the position can be marked "free"
00353     return true;
00354 }
00355 
00356 // ---------------------------------------------------------
00357 int GameData::selectPosition(int lastPosition) {
00358 
00359     int position, cnt = 0;
00360     bool goodPosition = false;
00361 
00362     // while a good position has not been found,
00363     while (!goodPosition) {
00364 
00365         // Select a random, but free, position.
00366         do {
00367               position = random.getLong(numTilesToGenerate);
00368             if (cnt++ > (numTilesToGenerate*numTilesToGenerate)) {
00369                 return -1; // bail
00370             }
00371         } while (!positionDepends[position].free);
00372 
00373         // Found one.
00374         goodPosition = true;
00375 
00376         // If there is a previous position to take into account,
00377         if (lastPosition != -1) {
00378 
00379             // Check the new position against the last one.
00380             for (int i = 0; i < 4; i++) {
00381                 if (positionDepends[position].place_dep[i] == lastPosition) {
00382                     goodPosition = false;  // not such a good position
00383                 }
00384             }
00385             for (int i = 0; i < 2; i++) {
00386                 if ((positionDepends[position].lhs_dep[i] == lastPosition) ||
00387                     (positionDepends[position].rhs_dep[i] == lastPosition)) {
00388                     goodPosition = false;  // not such a good position
00389                 }
00390             }
00391         }
00392     }
00393 
00394     return position;
00395 }
00396 
00397 // ---------------------------------------------------------
00398 void GameData::placeTile(int position, int tile) {
00399 
00400     // Install the tile in the specified position
00401     tilePositions[position].f = tile;
00402     putTile(tilePositions[position]);
00403 
00404     //added highlight?
00405     //setHighlightData(E,Y,X,0);
00406 
00407     // Update position dependency data
00408     positionDepends[position].filled = true;
00409     positionDepends[position].free = false;
00410 
00411     // Now examine the tiles near this to see if this makes them "free".
00412     int depend;
00413     for (int i = 0; i < 4; i++) {
00414         if ((depend = positionDepends[position].turn_dep[i]) != -1) {
00415             updateDepend(depend);
00416         }
00417     }
00418     for (int i = 0; i < 2; i++) {
00419         if ((depend = positionDepends[position].lhs_dep[i]) != -1) {
00420             updateDepend(depend);
00421         }
00422         if ((depend = positionDepends[position].rhs_dep[i]) != -1) {
00423             updateDepend(depend);
00424         }
00425     }
00426 }
00427 
00428 // ---------------------------------------------------------
00429 // Updates the free indicator in the dependency data for a position
00430 // based on whether the positions on which it depends are filled.
00431 void GameData::updateDepend(int position) {
00432 
00433     // If the position is valid and not filled
00434     if (position >= 0 && !positionDepends[position].filled) {
00435 
00436         // Check placement depends.  If they are not filled, the
00437         // position cannot become free.
00438         int depend;
00439         for (int i = 0; i < 4; i++) {
00440             if ((depend = positionDepends[position].place_dep[i]) != -1) {
00441                 if (!positionDepends[depend].filled) {
00442                     return ;
00443                 }
00444             }
00445         }
00446 
00447         // If position is first free on apparent horizontal, it is
00448         // now free to be filled.
00449           if (onlyFreeInLine(position)) {
00450               positionDepends[position].free = true;
00451             return;
00452         }
00453 
00454         // Assume no LHS positions to fill
00455         bool lfilled = false;
00456 
00457           // If positions to LHS
00458         if ((positionDepends[position].lhs_dep[0] != -1) ||
00459             (positionDepends[position].lhs_dep[1] != -1)) {
00460 
00461             // Assume LHS positions filled
00462             lfilled = true;
00463 
00464             for (int i = 0; i < 2; i++) {
00465                 if ((depend = positionDepends[position].lhs_dep[i]) != -1) {
00466                     if (!positionDepends[depend].filled) {
00467                          lfilled = false;
00468                     }
00469                 }
00470             }
00471         }
00472 
00473         // Assume no RHS positions to fill
00474         bool rfilled = false;
00475 
00476           // If positions to RHS
00477         if ((positionDepends[position].rhs_dep[0] != -1) ||
00478             (positionDepends[position].rhs_dep[1] != -1)) {
00479 
00480             // Assume LHS positions filled
00481             rfilled = true;
00482 
00483             for (int i = 0; i < 2; i++) {
00484                 if ((depend = positionDepends[position].rhs_dep[i]) != -1) {
00485                     if (!positionDepends[depend].filled) {
00486                         rfilled = false;
00487                     }
00488                 }
00489             }
00490         }
00491 
00492           // If positions to left or right are filled, this position
00493         // is now free to be filled.
00494           positionDepends[position].free = (lfilled || rfilled);
00495     }
00496 }
00497 
00498 
00499 // ---------------------------------------------------------
00500 bool GameData::generateStartPosition2() {
00501 
00502     // For each tile,
00503     for (int i = 0; i < numTilesToGenerate; i++) {
00504 
00505         // Get its basic position data
00506         int x = tilePositions[i].x;
00507         int y = tilePositions[i].y;
00508         int z = tilePositions[i].e;
00509 
00510         // Clear Game.Board at that position
00511         setBoardData(z,y,x,0);
00512 
00513         // Clear tile placed/free indicator(s).
00514         positionDepends[i].filled = false;
00515         positionDepends[i].free   = false;
00516 
00517         // Set tile face blank
00518         tilePositions[i].f = 254;
00519     }
00520 
00521     // If solvable games should be generated,
00522     //if (Prefs::solvableGames()) {
00523 
00524         if (generateSolvableGame()) {
00525             TileNum = MaxTileNum;
00526             return true;
00527         } else {
00528             return false;
00529         }
00530     //}
00531 
00532     // Initialise the faces to allocate. For the classic
00533     // dragon board there are 144 tiles. So we allocate and
00534     // randomise the assignment of 144 tiles. If there are > 144
00535     // tiles we will reallocate and re-randomise as we run out.
00536     // One advantage of this method is that the pairs to assign are
00537     // non-linear. In kmahjongg 0.4, If there were > 144 the same
00538     // allocation series was followed. So 154 = 144 + 10 rods.
00539     // 184 = 144 + 40 rods (20 pairs) which overwhemed the board
00540     // with rods and made deadlock games more likely.
00541 
00542     int remaining = numTilesToGenerate;
00543     randomiseFaces();
00544 
00545     for (int tile=0; tile <numTilesToGenerate; tile+=2) {
00546         int p1;
00547         int p2;
00548 
00549         if (remaining > 2) {
00550             p2 = p1 = random.getLong(remaining-2);
00551             int bail = 0;
00552             while (p1 == p2) {
00553                 p2 = random.getLong(remaining-2);
00554 
00555                 if (bail >= 100) {
00556                     if (p1 != p2) {
00557                         break;
00558                     }
00559                 }
00560                 if ((tilePositions[p1].y == tilePositions[p2].y) &&
00561                     (tilePositions[p1].e == tilePositions[p2].e)) {
00562                     // skip if on same y line
00563                     bail++;
00564                     p2=p1;
00565                     continue;
00566                 }
00567             }
00568         } else {
00569             p1 = 0;
00570             p2 = 1;
00571         }
00572         POSITION a, b;
00573         a = tilePositions[p1];
00574         b = tilePositions[p2];
00575         tilePositions[p1] = tilePositions[remaining - 1];
00576         tilePositions[p2] = tilePositions[remaining - 2];
00577         remaining -= 2;
00578 
00579         getFaces(a, b);
00580         putTile(a);
00581         putTile(b);
00582     }
00583 
00584     TileNum = MaxTileNum;
00585     return 1;
00586 }
00587 
00588 void GameData::getFaces(POSITION &a, POSITION &b) {
00589     a.f = tilePair[tilesUsed];
00590     b.f = tilePair[tilesUsed+1];
00591     tilesUsed += 2;
00592 
00593     if (tilesUsed >= 144) {
00594         randomiseFaces();
00595     }
00596 }
00597 
00598 void GameData::randomiseFaces() {
00599     int nr;
00600     int numAlloced=0;
00601     // stick in 144 tiles in pairsa.
00602 
00603         for( nr=0; nr<9*4; nr++)
00604         tilePair[numAlloced++] = TILE_CHARACTER+(nr/4); // 4*9 Tiles
00605         for( nr=0; nr<9*4; nr++)
00606         tilePair[numAlloced++] = TILE_BAMBOO+(nr/4); // 4*9 Tiles
00607         for( nr=0; nr<9*4; nr++)
00608         tilePair[numAlloced++] = TILE_ROD+(nr/4); // 4*9 Tiles
00609         for( nr=0; nr<4;   nr++)
00610         tilePair[numAlloced++] = TILE_FLOWER+nr;         // 4 Tiles
00611         for( nr=0; nr<4;   nr++)
00612         tilePair[numAlloced++] = TILE_SEASON+nr;         // 4 Tiles
00613         for( nr=0; nr<4*4; nr++)
00614         tilePair[numAlloced++] = TILE_WIND+(nr/4);  // 4*4 Tiles
00615         for( nr=0; nr<3*4; nr++)
00616         tilePair[numAlloced++] = TILE_DRAGON+(nr/4);     // 3*4 Tiles
00617 
00618 
00619     //randomise. Keep pairs together. Ie take two random
00620     //odd numbers (n,x) and swap n, n+1 with x, x+1
00621 
00622     int at=0;
00623     int to=0;
00624     for (int r=0; r<200; r++) {
00625 
00626 
00627         to=at;
00628         while (to==at) {
00629             to = random.getLong(144);
00630 
00631             if ((to & 1) != 0)
00632                 to--;
00633 
00634         }
00635         UCHAR tmp = tilePair[at];
00636         tilePair[at] = tilePair[to];
00637         tilePair[to] = tmp;
00638         tmp = tilePair[at+1];
00639         tilePair[at+1] = tilePair[to+1];
00640         tilePair[to+1] = tmp;
00641 
00642 
00643         at+=2;
00644         if (at >= 144)
00645             at =0;
00646     }
00647 
00648     tilesAllocated = numAlloced;
00649     tilesUsed = 0;
00650 }
00651 
00652 
00653 // ---------------------------------------------------------
00654 bool isFlower( UCHAR Tile )
00655 {
00656     return( Tile >= TILE_FLOWER  &&  Tile <=TILE_FLOWER+3 );
00657 }
00658 bool isSeason( UCHAR Tile )
00659 {
00660     return( Tile >= TILE_SEASON  &&  Tile <=TILE_SEASON+3 );
00661 }
00662 bool isBamboo(UCHAR t) {
00663     return( t >= TILE_BAMBOO && t <TILE_BAMBOO+9);
00664 }
00665 bool isCharacter(UCHAR t) {
00666     return( t <TILE_CHARACTER + 9);
00667 }
00668 bool isRod(UCHAR t) {
00669     return( t >= TILE_ROD && t <TILE_ROD + 9);
00670 }
00671 bool isDragon(UCHAR t) {
00672     return( t >= TILE_DRAGON && t < TILE_DRAGON +3);
00673 }
00674 bool isWind(UCHAR t) {
00675     return( t >= TILE_WIND && t < TILE_WIND +4);
00676 }
00677 
00678 bool GameData::isMatchingTile( POSITION& Pos1, POSITION& Pos2 )
00679 {
00680     // don't compare 'equal' positions
00681     if( memcmp( &Pos1, &Pos2, sizeof(POSITION) ) )
00682     {
00683         UCHAR FA = Pos1.f;
00684         UCHAR FB = Pos2.f;
00685 
00686         if( (FA == FB)
00687          || ( isFlower( FA ) && isFlower( FB ) )
00688          || ( isSeason( FA ) && isSeason( FB ) ) )
00689             return( true );
00690     }
00691     return( false );
00692 }
00693 
00694 // ---------------------------------------------------------
00695 void GameData::setRemovedTilePair(POSITION &a, POSITION &b) {
00696 
00697     if (isFlower(a.f)) {
00698         removedFlower[a.f-TILE_FLOWER]++;
00699         removedFlower[b.f-TILE_FLOWER]++;
00700         return;
00701     }
00702 
00703     if (isSeason(a.f)) {
00704         removedSeason[a.f-TILE_SEASON]++;
00705         removedSeason[b.f-TILE_SEASON]++;
00706         return;
00707     }
00708     if (isCharacter(a.f)) {
00709         removedCharacter[a.f - TILE_CHARACTER]+=2;
00710         return;
00711     }
00712 
00713     if (isBamboo(a.f)) {
00714         removedBamboo[a.f - TILE_BAMBOO]+=2;
00715         return;
00716     }
00717     if (isRod(a.f)) {
00718         removedRod[a.f - TILE_ROD]+=2;
00719         return;
00720     }
00721     if (isDragon(a.f)){
00722         removedDragon[a.f - TILE_DRAGON]+=2;
00723         return;
00724     }
00725     if (isWind(a.f)){
00726         removedWind[a.f - TILE_WIND]+=2;
00727         return;
00728     }
00729 }
00730 
00731 // ---------------------------------------------------------
00732 void GameData::clearRemovedTilePair(POSITION &a, POSITION &b) {
00733 
00734         if (isFlower(a.f)) {
00735                 removedFlower[a.f-TILE_FLOWER]--;
00736                 removedFlower[b.f-TILE_FLOWER]--;
00737                 return;
00738         }
00739 
00740         if (isSeason(a.f)) {
00741                 removedSeason[a.f-TILE_SEASON]--;
00742                 removedSeason[b.f-TILE_SEASON]--;
00743                 return;
00744         }
00745         if (isCharacter(a.f)) {
00746                 removedCharacter[a.f - TILE_CHARACTER]-=2;
00747                 return;
00748         }
00749 
00750         if (isBamboo(a.f)) {
00751                 removedBamboo[a.f - TILE_BAMBOO]-=2;
00752                 return;
00753         }
00754         if (isRod(a.f)){
00755                 removedRod[a.f - TILE_ROD]-=2;
00756                 return;
00757         }
00758         if (isDragon(a.f)){
00759                 removedDragon[a.f - TILE_DRAGON]-=2;
00760                 return;
00761         }
00762         if (isWind(a.f)){
00763                 removedWind[a.f - TILE_WIND]-=2;
00764                 return;
00765         }
00766 }
00767 
00768 
00769 // ---------------------------------------------------------
00770 void GameData::initialiseRemovedTiles() {
00771     for (int pos=0; pos<9; pos++) {
00772         removedCharacter[pos]=0;
00773         removedBamboo[pos]=0;
00774         removedRod[pos]=0;
00775         removedDragon[pos %3] = 0;
00776         removedFlower[pos % 4] = 0;
00777         removedWind[pos % 4] = 0;
00778         removedSeason[pos % 4] = 0;
00779 
00780     }
00781 
00782 }
00783 
00784 
00785 // ---------------------------------------------------------
00786 bool GameData::findMove( POSITION& posA, POSITION& posB )
00787 {
00788     short Pos_Ende = MaxTileNum;  // Ende der PosTable
00789 
00790     for( short E=0; E< m_depth; E++ )
00791     {
00792         for( short Y=0; Y< m_height-1; Y++ )
00793         {
00794             for( short X=0; X< m_width-1; X++ )
00795             {
00796                 if( MaskData(E,Y,X) != (UCHAR) '1' )
00797                     continue;
00798                 if( ! BoardData(E,Y,X) )
00799                     continue;
00800                 if( E < m_depth-1 )
00801                 {
00802                     if( BoardData(E+1,Y,X) || BoardData(E+1,Y+1,X) ||
00803                         BoardData(E+1,Y,X+1) || BoardData(E+1,Y+1,X+1) )
00804                         continue;
00805                 }
00806                 if( X< m_width-2 && (BoardData(E,Y,X-1) || BoardData(E,Y+1,X-1)) &&
00807                                               (BoardData(E,Y,X+2) || BoardData(E,Y+1,X+2)) )
00808                     continue;
00809 
00810                 Pos_Ende--;
00811                 PosTable[Pos_Ende].e = E;
00812                 PosTable[Pos_Ende].y = Y;
00813                 PosTable[Pos_Ende].x = X;
00814                 PosTable[Pos_Ende].f = BoardData(E,Y,X);
00815 
00816 
00817 
00818 
00819             }
00820         }
00821     }
00822 
00823     short iPosCount = 0;  // Hier Anzahl der gefunden Paare merken
00824 
00825     // The new tile layout with non-contiguos horizantle spans
00826     // can lead to huge numbers of matching pairs being exposed.
00827     // we alter the loop to bail out when BoardLayout::maxTiles/2 pairs are found
00828     // (or less);
00829     while( Pos_Ende < MaxTileNum-1 && iPosCount < m_maxTiles-2)
00830     {
00831         for( short Pos = Pos_Ende+1; Pos < MaxTileNum; Pos++)
00832         {
00833             if( isMatchingTile(PosTable[Pos], PosTable[Pos_Ende]) )
00834             {
00835         if (iPosCount < m_maxTiles-2) {
00836                     PosTable[iPosCount++] = PosTable[Pos_Ende];
00837                     PosTable[iPosCount++] = PosTable[Pos];
00838         }
00839             }
00840         }
00841         Pos_Ende++;
00842     }
00843 
00844     if( iPosCount>=2 )
00845     {
00846         random.setSeed(0); // WABA: Why is the seed reset?
00847         short Pos = random.getLong(iPosCount) & -2;  // Gerader Wert
00848         posA = PosTable[Pos];
00849         posB = PosTable[Pos+1];
00850 
00851         return( true );
00852     }
00853     else
00854         return( false );
00855 }
00856 
00857 int GameData::moveCount( )
00858 {
00859     short Pos_Ende = MaxTileNum;  // end of PosTable
00860 
00861     for( short E=0; E< m_depth; E++ )
00862     {
00863         for( short Y=0; Y< m_height-1; Y++ )
00864         {
00865             for( short X=0; X< m_width-1; X++ )
00866             {
00867                 if( MaskData(E,Y,X) != (UCHAR) '1' )
00868                     continue;
00869                 if( ! BoardData(E,Y,X) )
00870                     continue;
00871                 if( E < m_depth-1 )
00872                 {
00873                     if( BoardData(E+1,Y,X) || BoardData(E+1,Y+1,X) ||
00874                         BoardData(E+1,Y,X+1) || BoardData(E+1,Y+1,X+1) )
00875                         continue;
00876                 }
00877                 if( X< m_width-2 && (BoardData(E,Y,X-1) || BoardData(E,Y+1,X-1)) &&
00878                                               (BoardData(E,Y,X+2) || BoardData(E,Y+1,X+2)) )
00879                     continue;
00880 
00881                 Pos_Ende--;
00882                 PosTable[Pos_Ende].e = E;
00883                 PosTable[Pos_Ende].y = Y;
00884                 PosTable[Pos_Ende].x = X;
00885                 PosTable[Pos_Ende].f = BoardData(E,Y,X);
00886 
00887             }
00888         }
00889     }
00890 
00891     short iPosCount = 0;  // store number of pairs found
00892 
00893     while( Pos_Ende < MaxTileNum-1 && iPosCount < m_maxTiles-2)
00894     {
00895         for( short Pos = Pos_Ende+1; Pos < MaxTileNum; Pos++)
00896         {
00897             if( isMatchingTile(PosTable[Pos], PosTable[Pos_Ende]) )
00898             {
00899         if (iPosCount < m_maxTiles-2) {
00900                     PosTable[iPosCount++] = PosTable[Pos_Ende];
00901                     PosTable[iPosCount++] = PosTable[Pos];
00902         }
00903             }
00904         }
00905         Pos_Ende++;
00906     }
00907 
00908     return iPosCount/2;
00909 }
00910 
00911 
00912 
00913 
00914 // ---------------------------------------------------------
00915 short GameData::findAllMatchingTiles( POSITION& posA )
00916 {
00917     short Pos = 0;
00918 
00919     for( short E=0; E< m_depth; E++ )
00920     {
00921         for( short Y=0; Y< m_height-1; Y++ )
00922         {
00923             for( short X=0; X< m_width-1; X++ )
00924             {
00925                 if( MaskData(E,Y,X) != (UCHAR) '1' )
00926                     continue;
00927                 if( ! BoardData(E,Y,X) )
00928                     continue;
00929                 if( E < m_depth-1 )
00930                 {
00931                     if( BoardData(E+1,Y,X) || BoardData(E+1,Y+1,X) ||
00932                         BoardData(E+1,Y,X+1) || BoardData(E+1,Y+1,X+1) )
00933                         continue;
00934                 }
00935                 if( X< m_width-2 && (BoardData(E,Y,X-1) || BoardData(E,Y+1,X-1)) &&
00936                                               (BoardData(E,Y,X+2) || BoardData(E,Y+1,X+2)) )
00937                     continue;
00938 
00939                 PosTable[Pos].e = E;
00940                 PosTable[Pos].y = Y;
00941                 PosTable[Pos].x = X;
00942                 PosTable[Pos].f = BoardData(E,Y,X);
00943 
00944                 if( isMatchingTile(posA, PosTable[Pos]) )
00945                     Pos++;
00946             }
00947         }
00948     }
00949     return Pos;
00950 }
00951 
00952 bool GameData::loadFromStream(QDataStream & in)
00953 {
00954   in >> Board;
00955   in >> Mask;
00956   in >> Highlight;
00957   in >> allow_undo;
00958   in >> allow_redo;
00959   in >> TileNum;
00960   in >> MaxTileNum;
00961   
00962   //Read list count
00963   in >> m_maxTiles;
00964 
00965   //Reconstruct the MoveList
00966   for (int i = 0; i < m_maxTiles; ++i) {
00967     POSITION thispos;
00968     in >> thispos.e;
00969     in >> thispos.y;
00970     in >> thispos.x;
00971     in >> thispos.f;
00972     setMoveListData( i, thispos);
00973   }
00974   return true;
00975 }
00976 
00977 bool GameData::saveToStream(QDataStream & out)
00978 {
00979   out << Board;
00980   out << Mask;
00981   out << Highlight;
00982   out << allow_undo;
00983   out << allow_redo;
00984   out << TileNum;
00985   out << MaxTileNum;
00986   //write the size of our lists
00987   out << m_maxTiles;
00988   //and then write all position components for the MoveList
00989   for (int i = 0; i < m_maxTiles; ++i) {
00990     POSITION thispos = MoveList.at(i);
00991     out << (quint16) thispos.e;
00992     out << (quint16) thispos.y;
00993     out << (quint16) thispos.x;
00994     out << (quint16) thispos.f;
00995   }
00996 
00997   return true;
00998 }
00999 
01000 void GameData::shuffle() {
01001   int count = 0;
01002     // copy positions and faces of the remaining tiles into
01003     // the pos table
01004   for (int e=0; e<m_depth; e++) {
01005     for (int y=0; y<m_height; y++) {
01006       for (int x=0; x<m_width; x++) {
01007         if (BoardData(e,y,x) && MaskData(e,y,x) == '1') {
01008           PosTable[count].e = e;
01009           PosTable[count].y = y;
01010           PosTable[count].x = x;
01011           PosTable[count].f = BoardData(e,y,x);
01012           count++;
01013         }
01014       }
01015     }
01016 
01017   }
01018 
01019 
01020     // now lets randomise the faces, selecting 400 pairs at random and
01021     // swapping the faces.
01022   for (int ran=0; ran < 400; ran++) {
01023     int pos1 = random.getLong(count);
01024     int pos2 = random.getLong(count);
01025     if (pos1 == pos2)
01026       continue;
01027     BYTE f = PosTable[pos1].f;
01028     PosTable[pos1].f = PosTable[pos2].f;
01029     PosTable[pos2].f = f;
01030   }
01031 
01032     // put the rearranged tiles back.
01033   for (int p=0; p<count; p++)
01034     putTile(PosTable[p]);
01035 }

kmahjongg

Skip menu "kmahjongg"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members

API Reference

Skip menu "API Reference"
  • kblackbox
  • kgoldrunner
  • kmahjongg
  • ksquares
  • libkdegames
  •   highscore
  •   kgame
  •   kggzgames
  •   kggzmod
  •   kggznet
  • libkmahjongg
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal