SVN_SILENT made messages (.desktop file)
[kdegames.git] / kmahjongg / GameData.cpp
blob513dad4afc4da242bff28f2930932dcadcd92c77
1 /*
2 Copyright (C) 2006 Mauricio Piacentini <mauricio@tabuleiro.com>
4 Kmahjongg is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 #include "GameData.h"
20 #include <QtDebug>
23 GameData::GameData (BoardLayout * boardlayout) {
24 m_width = boardlayout->m_width;
25 m_height = boardlayout->m_height;
26 m_depth = boardlayout->m_depth;
27 m_maxTiles = (m_width*m_height*m_depth)/4;
29 Highlight = QByteArray(m_width*m_height*m_depth, 0);
30 Mask = QByteArray(m_width*m_height*m_depth, 0);
31 Board = QByteArray(m_width*m_height*m_depth, 0);
32 POSITION e; //constructor initializes it to 0
33 MoveList = QVector<POSITION>(m_maxTiles, e);
34 tilePositions = QVector<POSITION>(m_maxTiles, e);
35 PosTable = QVector<POSITION>(m_maxTiles, e);
36 positionDepends = QVector<DEPENDENCY>(m_maxTiles);
38 //Copy board layout over
39 boardlayout->copyBoardLayout((UCHAR *) getMaskBytes(), MaxTileNum);
42 GameData::~GameData () {
46 void GameData::putTile( short e, short y, short x, UCHAR f ){
47 setBoardData(e,y,x,f);
48 setBoardData(e,y+1,x,f);
49 setBoardData(e,y+1,x+1,f);
50 setBoardData(e,y,x+1,f);
53 bool GameData::tilePresent(int z, int y, int x) {
54 if ((y<0)||(x<0)||(z<0)||(y>m_height-1)||(x>m_width-1)||(z>m_depth-1)) return false;
55 return(BoardData(z,y,x)!=0 && MaskData(z,y,x) == '1');
58 bool GameData::partTile(int z, int y, int x) {
59 return (BoardData(z,y,x) != 0);
62 UCHAR GameData::MaskData(short z, short y, short x){
63 if ((y<0)||(x<0)||(z<0)||(y>m_height-1)||(x>m_width-1)||(z>m_depth-1)) return 0;
64 return Mask.at((z*m_width*m_height)+(y*m_width)+x);
67 UCHAR GameData::HighlightData(short z, short y, short x){
68 if ((y<0)||(x<0)||(z<0)||(y>m_height-1)||(x>m_width-1)||(z>m_depth-1)) return 0;
69 return Highlight.at((z*m_width*m_height)+(y*m_width)+x);
72 void GameData::setHighlightData(short z, short y, short x, UCHAR value) {
73 if ((y<0)||(x<0)||(z<0)||(y>m_height-1)||(x>m_width-1)||(z>m_depth-1)) return ;
74 Highlight[(z*m_width*m_height)+(y*m_width)+x] = value;
77 UCHAR GameData::BoardData(short z, short y, short x){
78 if ((y<0)||(x<0)||(z<0)||(y>m_height-1)||(x>m_width-1)||(z>m_depth-1)) return 0;
79 return Board.at((z*m_width*m_height)+(y*m_width)+x);
82 void GameData::setBoardData(short z, short y, short x, UCHAR value) {
83 if ((y<0)||(x<0)||(z<0)||(y>m_height-1)||(x>m_width-1)||(z>m_depth-1)) return ;
84 Board[(z*m_width*m_height)+(y*m_width)+x] = value;
87 POSITION& GameData::MoveListData(short i) {
88 if ((i>=MoveList.size())|| (i<0)) {
89 qDebug() << "Attempt to access GameData::MoveListData with invalid index";
90 i=0 ;
92 return MoveList[i];
95 void GameData::setMoveListData(short i, POSITION& value){
96 if ((i>=MoveList.size()) || (i<0)) return ;
97 MoveList[i] = value;
100 //Game generation
102 // ---------------------------------------------------------
103 // Generate the position data for the layout from contents of Game.Map.
104 void GameData::generateTilePositions() {
106 numTilesToGenerate = 0;
108 for (int z=0; z< m_depth; z++) {
109 for (int y=0; y< m_height; y++) {
110 for (int x=0; x< m_width; x++) {
111 setBoardData(z,y,x,0);
112 if (MaskData(z,y,x) == '1') {
113 tilePositions[numTilesToGenerate].x = x;
114 tilePositions[numTilesToGenerate].y = y;
115 tilePositions[numTilesToGenerate].e = z;
116 tilePositions[numTilesToGenerate].f = 254;
117 numTilesToGenerate++;
124 // ---------------------------------------------------------
125 // Generate the dependency data for the layout from the position data.
126 // Note that the coordinates of each tile in tilePositions are those of
127 // the upper left quarter of the tile.
128 void GameData::generatePositionDepends() {
130 // For each tile,
131 for (int i = 0; i < numTilesToGenerate; i++) {
133 // Get its basic position data
134 int x = tilePositions[i].x;
135 int y = tilePositions[i].y;
136 int z = tilePositions[i].e;
138 // LHS dependencies
139 positionDepends[i].lhs_dep[0] = tileAt(x-1, y, z);
140 positionDepends[i].lhs_dep[1] = tileAt(x-1, y+1, z);
142 // Make them unique
143 if (positionDepends[i].lhs_dep[1] == positionDepends[i].lhs_dep[0]) {
144 positionDepends[i].lhs_dep[1] = -1;
147 // RHS dependencies
148 positionDepends[i].rhs_dep[0] = tileAt(x+2, y, z);
149 positionDepends[i].rhs_dep[1] = tileAt(x+2, y+1, z);
151 // Make them unique
152 if (positionDepends[i].rhs_dep[1] == positionDepends[i].rhs_dep[0]) {
153 positionDepends[i].rhs_dep[1] = -1;
156 // Turn dependencies
157 positionDepends[i].turn_dep[0] = tileAt(x, y, z+1);
158 positionDepends[i].turn_dep[1] = tileAt(x+1, y, z+1);
159 positionDepends[i].turn_dep[2] = tileAt(x+1, y+1, z+1);
160 positionDepends[i].turn_dep[3] = tileAt(x, y+1, z+1);
162 // Make them unique
163 for (int j = 0; j < 3; j++) {
164 for (int k = j+1; k < 4; k++) {
165 if (positionDepends[i].turn_dep[j] ==
166 positionDepends[i].turn_dep[k]) {
167 positionDepends[i].turn_dep[k] = -1;
172 // Placement dependencies
173 positionDepends[i].place_dep[0] = tileAt(x, y, z-1);
174 positionDepends[i].place_dep[1] = tileAt(x+1, y, z-1);
175 positionDepends[i].place_dep[2] = tileAt(x+1, y+1, z-1);
176 positionDepends[i].place_dep[3] = tileAt(x, y+1, z-1);
178 // Make them unique
179 for (int j = 0; j < 3; j++) {
180 for (int k = j+1; k < 4; k++) {
181 if (positionDepends[i].place_dep[j] ==
182 positionDepends[i].place_dep[k]) {
183 positionDepends[i].place_dep[k] = -1;
188 // Filled and free indicators.
189 positionDepends[i].filled = false;
190 positionDepends[i].free = false;
194 // ---------------------------------------------------------
195 // x, y, z are the coordinates of a *quarter* tile. This returns the
196 // index (in positions) of the tile at those coordinates or -1 if there
197 // is no tile at those coordinates. Note that the coordinates of each
198 // tile in positions are those of the upper left quarter of the tile.
199 int GameData::tileAt(int x, int y, int z) {
201 for (int i = 0; i < numTilesToGenerate; i++) {
202 if (tilePositions[i].e == z) {
203 if ((tilePositions[i].x == x && tilePositions[i].y == y) ||
204 (tilePositions[i].x == x-1 && tilePositions[i].y == y) ||
205 (tilePositions[i].x == x-1 && tilePositions[i].y == y-1) ||
206 (tilePositions[i].x == x && tilePositions[i].y == y-1)) {
208 return i;
212 return -1;
216 // ---------------------------------------------------------
217 bool GameData::generateSolvableGame() {
219 // Initially we want to mark positions on layer 0 so that we have only
220 // one free position per apparent horizontal line.
221 for (int i = 0; i < numTilesToGenerate; i++) {
223 // Pick a random tile on layer 0
224 int position, cnt = 0;
225 do {
226 position = (int) random.getLong(numTilesToGenerate);
227 if (cnt++ > (numTilesToGenerate*numTilesToGenerate)) {
228 return false; // bail
230 } while (tilePositions[position].e != 0);
232 // If there are no other free positions on the same apparent
233 // horizontal line, we can mark that position as free.
234 if (onlyFreeInLine(position)) {
235 positionDepends[position].free = true;
239 // Check to make sure we really got them all. Very important for
240 // this algorithm.
241 for (int i = 0; i < numTilesToGenerate; i++) {
242 if (tilePositions[i].e == 0 && onlyFreeInLine(i)) {
243 positionDepends[i].free = true;
247 // Get ready to place the tiles
248 int lastPosition = -1;
249 int position = -1;
250 int position2 = -1;
252 // For each position,
253 for (int i = 0; i < numTilesToGenerate; i++) {
255 // If this is the first tile in a 144 tile set,
256 if ((i % 144) == 0) {
258 // Initialise the faces to allocate. For the classic
259 // dragon board there are 144 tiles. So we allocate and
260 // randomise the assignment of 144 tiles. If there are > 144
261 // tiles we will reallocate and re-randomise as we run out.
262 // One advantage of this method is that the pairs to assign are
263 // non-linear. In kmahjongg 0.4, If there were > 144 the same
264 // allocation series was followed. So 154 = 144 + 10 rods.
265 // 184 = 144 + 40 rods (20 pairs) which overwhemed the board
266 // with rods and made deadlock games more likely.
267 randomiseFaces();
270 // If this is the first half of a pair, there is no previous
271 // position for the pair.
272 if ((i & 1) == 0) {
273 lastPosition = -1;
276 // Select a position for the tile, relative to the position of
277 // the last tile placed.
278 if ((position = selectPosition(lastPosition)) < 0) {
279 return false; // bail
281 if (i < numTilesToGenerate-1) {
282 if ((position2 = selectPosition(lastPosition)) < 0) {
283 return false; // bail
285 if (tilePositions[position2].e > tilePositions[position].e) {
286 position = position2; // higher is better
290 // Place the tile.
291 placeTile(position, tilePair[i % 144]);
293 // Remember the position
294 lastPosition = position;
297 // The game is solvable.
298 return true;
301 // ---------------------------------------------------------
302 // Determines whether it is ok to mark this position as "free" because
303 // there are no other positions marked "free" in its apparent horizontal
304 // line.
305 bool GameData::onlyFreeInLine(int position) {
307 int i, i0, w;
308 int lin, rin, out;
309 //static int nextLeft[m_maxTiles];
310 //static int nextRight[m_maxTiles];
311 QVector<int> nextLeft = QVector<int>(m_maxTiles, 0);
312 QVector<int> nextRight = QVector<int>(m_maxTiles, 0);
314 /* Check left, starting at position */
315 lin = 0;
316 out = 0;
317 nextLeft[lin++] = position;
318 do {
319 w = nextLeft[out++];
320 if (positionDepends[w].free || positionDepends[w].filled) {
321 return false;
323 if ((i = positionDepends[w].lhs_dep[0]) != -1) {
324 nextLeft[lin++] = i;
326 i0 = i;
327 if ((i = positionDepends[w].lhs_dep[1]) != -1 && i0 != i) {
328 nextLeft[lin++] = i;
331 while (lin > out) ;
333 /* Check right, starting at position */
334 rin = 0;
335 out = 0;
336 nextRight[rin++] = position;
337 do {
338 w = nextRight[out++];
339 if (positionDepends[w].free || positionDepends[w].filled) {
340 return false;
342 if ((i = positionDepends[w].rhs_dep[0]) != -1) {
343 nextRight[rin++] = i;
345 i0 = i;
346 if ((i = positionDepends[w].rhs_dep[1]) != -1 && i0 != i) {
347 nextRight[rin++] = i;
350 while (rin > out) ;
352 // Here, the position can be marked "free"
353 return true;
356 // ---------------------------------------------------------
357 int GameData::selectPosition(int lastPosition) {
359 int position, cnt = 0;
360 bool goodPosition = false;
362 // while a good position has not been found,
363 while (!goodPosition) {
365 // Select a random, but free, position.
366 do {
367 position = random.getLong(numTilesToGenerate);
368 if (cnt++ > (numTilesToGenerate*numTilesToGenerate)) {
369 return -1; // bail
371 } while (!positionDepends[position].free);
373 // Found one.
374 goodPosition = true;
376 // If there is a previous position to take into account,
377 if (lastPosition != -1) {
379 // Check the new position against the last one.
380 for (int i = 0; i < 4; i++) {
381 if (positionDepends[position].place_dep[i] == lastPosition) {
382 goodPosition = false; // not such a good position
385 for (int i = 0; i < 2; i++) {
386 if ((positionDepends[position].lhs_dep[i] == lastPosition) ||
387 (positionDepends[position].rhs_dep[i] == lastPosition)) {
388 goodPosition = false; // not such a good position
394 return position;
397 // ---------------------------------------------------------
398 void GameData::placeTile(int position, int tile) {
400 // Install the tile in the specified position
401 tilePositions[position].f = tile;
402 putTile(tilePositions[position]);
404 //added highlight?
405 //setHighlightData(E,Y,X,0);
407 // Update position dependency data
408 positionDepends[position].filled = true;
409 positionDepends[position].free = false;
411 // Now examine the tiles near this to see if this makes them "free".
412 int depend;
413 for (int i = 0; i < 4; i++) {
414 if ((depend = positionDepends[position].turn_dep[i]) != -1) {
415 updateDepend(depend);
418 for (int i = 0; i < 2; i++) {
419 if ((depend = positionDepends[position].lhs_dep[i]) != -1) {
420 updateDepend(depend);
422 if ((depend = positionDepends[position].rhs_dep[i]) != -1) {
423 updateDepend(depend);
428 // ---------------------------------------------------------
429 // Updates the free indicator in the dependency data for a position
430 // based on whether the positions on which it depends are filled.
431 void GameData::updateDepend(int position) {
433 // If the position is valid and not filled
434 if (position >= 0 && !positionDepends[position].filled) {
436 // Check placement depends. If they are not filled, the
437 // position cannot become free.
438 int depend;
439 for (int i = 0; i < 4; i++) {
440 if ((depend = positionDepends[position].place_dep[i]) != -1) {
441 if (!positionDepends[depend].filled) {
442 return ;
447 // If position is first free on apparent horizontal, it is
448 // now free to be filled.
449 if (onlyFreeInLine(position)) {
450 positionDepends[position].free = true;
451 return;
454 // Assume no LHS positions to fill
455 bool lfilled = false;
457 // If positions to LHS
458 if ((positionDepends[position].lhs_dep[0] != -1) ||
459 (positionDepends[position].lhs_dep[1] != -1)) {
461 // Assume LHS positions filled
462 lfilled = true;
464 for (int i = 0; i < 2; i++) {
465 if ((depend = positionDepends[position].lhs_dep[i]) != -1) {
466 if (!positionDepends[depend].filled) {
467 lfilled = false;
473 // Assume no RHS positions to fill
474 bool rfilled = false;
476 // If positions to RHS
477 if ((positionDepends[position].rhs_dep[0] != -1) ||
478 (positionDepends[position].rhs_dep[1] != -1)) {
480 // Assume LHS positions filled
481 rfilled = true;
483 for (int i = 0; i < 2; i++) {
484 if ((depend = positionDepends[position].rhs_dep[i]) != -1) {
485 if (!positionDepends[depend].filled) {
486 rfilled = false;
492 // If positions to left or right are filled, this position
493 // is now free to be filled.
494 positionDepends[position].free = (lfilled || rfilled);
499 // ---------------------------------------------------------
500 bool GameData::generateStartPosition2() {
502 // For each tile,
503 for (int i = 0; i < numTilesToGenerate; i++) {
505 // Get its basic position data
506 int x = tilePositions[i].x;
507 int y = tilePositions[i].y;
508 int z = tilePositions[i].e;
510 // Clear Game.Board at that position
511 setBoardData(z,y,x,0);
513 // Clear tile placed/free indicator(s).
514 positionDepends[i].filled = false;
515 positionDepends[i].free = false;
517 // Set tile face blank
518 tilePositions[i].f = 254;
521 // If solvable games should be generated,
522 //if (Prefs::solvableGames()) {
524 if (generateSolvableGame()) {
525 TileNum = MaxTileNum;
526 return true;
527 } else {
528 return false;
532 // Initialise the faces to allocate. For the classic
533 // dragon board there are 144 tiles. So we allocate and
534 // randomise the assignment of 144 tiles. If there are > 144
535 // tiles we will reallocate and re-randomise as we run out.
536 // One advantage of this method is that the pairs to assign are
537 // non-linear. In kmahjongg 0.4, If there were > 144 the same
538 // allocation series was followed. So 154 = 144 + 10 rods.
539 // 184 = 144 + 40 rods (20 pairs) which overwhemed the board
540 // with rods and made deadlock games more likely.
542 int remaining = numTilesToGenerate;
543 randomiseFaces();
545 for (int tile=0; tile <numTilesToGenerate; tile+=2) {
546 int p1;
547 int p2;
549 if (remaining > 2) {
550 p2 = p1 = random.getLong(remaining-2);
551 int bail = 0;
552 while (p1 == p2) {
553 p2 = random.getLong(remaining-2);
555 if (bail >= 100) {
556 if (p1 != p2) {
557 break;
560 if ((tilePositions[p1].y == tilePositions[p2].y) &&
561 (tilePositions[p1].e == tilePositions[p2].e)) {
562 // skip if on same y line
563 bail++;
564 p2=p1;
565 continue;
568 } else {
569 p1 = 0;
570 p2 = 1;
572 POSITION a, b;
573 a = tilePositions[p1];
574 b = tilePositions[p2];
575 tilePositions[p1] = tilePositions[remaining - 1];
576 tilePositions[p2] = tilePositions[remaining - 2];
577 remaining -= 2;
579 getFaces(a, b);
580 putTile(a);
581 putTile(b);
584 TileNum = MaxTileNum;
585 return 1;
588 void GameData::getFaces(POSITION &a, POSITION &b) {
589 a.f = tilePair[tilesUsed];
590 b.f = tilePair[tilesUsed+1];
591 tilesUsed += 2;
593 if (tilesUsed >= 144) {
594 randomiseFaces();
598 void GameData::randomiseFaces() {
599 int nr;
600 int numAlloced=0;
601 // stick in 144 tiles in pairsa.
603 for( nr=0; nr<9*4; ++nr)
604 tilePair[++numAlloced] = TILE_CHARACTER+(nr/4); // 4*9 Tiles
605 for( nr=0; nr<9*4; ++nr)
606 tilePair[++numAlloced] = TILE_BAMBOO+(nr/4); // 4*9 Tiles
607 for( nr=0; nr<9*4; ++nr)
608 tilePair[++numAlloced] = TILE_ROD+(nr/4); // 4*9 Tiles
609 for( nr=0; nr<4; ++nr)
610 tilePair[++numAlloced] = TILE_FLOWER+nr; // 4 Tiles
611 for( nr=0; nr<4; ++nr)
612 tilePair[++numAlloced] = TILE_SEASON+nr; // 4 Tiles
613 for( nr=0; nr<4*4; ++nr)
614 tilePair[++numAlloced] = TILE_WIND+(nr/4); // 4*4 Tiles
615 for( nr=0; nr<3*4; ++nr)
616 tilePair[++numAlloced] = TILE_DRAGON+(nr/4); // 3*4 Tiles
619 //randomise. Keep pairs together. Ie take two random
620 //odd numbers (n,x) and swap n, n+1 with x, x+1
622 int at=0;
623 int to=0;
624 for (int r=0; r<200; r++) {
627 to=at;
628 while (to==at) {
629 to = random.getLong(144);
631 if ((to & 1) != 0)
632 to--;
635 UCHAR tmp = tilePair[at];
636 tilePair[at] = tilePair[to];
637 tilePair[to] = tmp;
638 tmp = tilePair[at+1];
639 tilePair[at+1] = tilePair[to+1];
640 tilePair[to+1] = tmp;
643 at+=2;
644 if (at >= 144)
645 at =0;
648 tilesAllocated = numAlloced;
649 tilesUsed = 0;
653 // ---------------------------------------------------------
654 bool isFlower( UCHAR Tile )
656 return( Tile >= TILE_FLOWER && Tile <=TILE_FLOWER+3 );
658 bool isSeason( UCHAR Tile )
660 return( Tile >= TILE_SEASON && Tile <=TILE_SEASON+3 );
662 bool isBamboo(UCHAR t) {
663 return( t >= TILE_BAMBOO && t <TILE_BAMBOO+9);
665 bool isCharacter(UCHAR t) {
666 return( t <TILE_CHARACTER + 9);
668 bool isRod(UCHAR t) {
669 return( t >= TILE_ROD && t <TILE_ROD + 9);
671 bool isDragon(UCHAR t) {
672 return( t >= TILE_DRAGON && t < TILE_DRAGON +3);
674 bool isWind(UCHAR t) {
675 return( t >= TILE_WIND && t < TILE_WIND +4);
678 bool GameData::isMatchingTile( POSITION& Pos1, POSITION& Pos2 )
680 // don't compare 'equal' positions
681 if( memcmp( &Pos1, &Pos2, sizeof(POSITION) ) )
683 UCHAR FA = Pos1.f;
684 UCHAR FB = Pos2.f;
686 if( (FA == FB)
687 || ( isFlower( FA ) && isFlower( FB ) )
688 || ( isSeason( FA ) && isSeason( FB ) ) )
689 return( true );
691 return( false );
694 // ---------------------------------------------------------
695 void GameData::setRemovedTilePair(POSITION &a, POSITION &b) {
697 if (isFlower(a.f)) {
698 removedFlower[a.f-TILE_FLOWER]++;
699 removedFlower[b.f-TILE_FLOWER]++;
700 return;
703 if (isSeason(a.f)) {
704 removedSeason[a.f-TILE_SEASON]++;
705 removedSeason[b.f-TILE_SEASON]++;
706 return;
708 if (isCharacter(a.f)) {
709 removedCharacter[a.f - TILE_CHARACTER]+=2;
710 return;
713 if (isBamboo(a.f)) {
714 removedBamboo[a.f - TILE_BAMBOO]+=2;
715 return;
717 if (isRod(a.f)) {
718 removedRod[a.f - TILE_ROD]+=2;
719 return;
721 if (isDragon(a.f)){
722 removedDragon[a.f - TILE_DRAGON]+=2;
723 return;
725 if (isWind(a.f)){
726 removedWind[a.f - TILE_WIND]+=2;
727 return;
731 // ---------------------------------------------------------
732 void GameData::clearRemovedTilePair(POSITION &a, POSITION &b) {
734 if (isFlower(a.f)) {
735 removedFlower[a.f-TILE_FLOWER]--;
736 removedFlower[b.f-TILE_FLOWER]--;
737 return;
740 if (isSeason(a.f)) {
741 removedSeason[a.f-TILE_SEASON]--;
742 removedSeason[b.f-TILE_SEASON]--;
743 return;
745 if (isCharacter(a.f)) {
746 removedCharacter[a.f - TILE_CHARACTER]-=2;
747 return;
750 if (isBamboo(a.f)) {
751 removedBamboo[a.f - TILE_BAMBOO]-=2;
752 return;
754 if (isRod(a.f)){
755 removedRod[a.f - TILE_ROD]-=2;
756 return;
758 if (isDragon(a.f)){
759 removedDragon[a.f - TILE_DRAGON]-=2;
760 return;
762 if (isWind(a.f)){
763 removedWind[a.f - TILE_WIND]-=2;
764 return;
769 // ---------------------------------------------------------
770 void GameData::initialiseRemovedTiles() {
771 for (int pos=0; pos<9; pos++) {
772 removedCharacter[pos]=0;
773 removedBamboo[pos]=0;
774 removedRod[pos]=0;
775 removedDragon[pos %3] = 0;
776 removedFlower[pos % 4] = 0;
777 removedWind[pos % 4] = 0;
778 removedSeason[pos % 4] = 0;
785 // ---------------------------------------------------------
786 bool GameData::findMove( POSITION& posA, POSITION& posB )
788 short Pos_Ende = MaxTileNum; // Ende der PosTable
790 for( short E=0; E< m_depth; E++ )
792 for( short Y=0; Y< m_height-1; Y++ )
794 for( short X=0; X< m_width-1; X++ )
796 if( MaskData(E,Y,X) != (UCHAR) '1' )
797 continue;
798 if( ! BoardData(E,Y,X) )
799 continue;
800 if( E < m_depth-1 )
802 if( BoardData(E+1,Y,X) || BoardData(E+1,Y+1,X) ||
803 BoardData(E+1,Y,X+1) || BoardData(E+1,Y+1,X+1) )
804 continue;
806 if( X< m_width-2 && (BoardData(E,Y,X-1) || BoardData(E,Y+1,X-1)) &&
807 (BoardData(E,Y,X+2) || BoardData(E,Y+1,X+2)) )
808 continue;
810 Pos_Ende--;
811 PosTable[Pos_Ende].e = E;
812 PosTable[Pos_Ende].y = Y;
813 PosTable[Pos_Ende].x = X;
814 PosTable[Pos_Ende].f = BoardData(E,Y,X);
823 short iPosCount = 0; // Hier Anzahl der gefunden Paare merken
825 // The new tile layout with non-contiguos horizantle spans
826 // can lead to huge numbers of matching pairs being exposed.
827 // we alter the loop to bail out when BoardLayout::maxTiles/2 pairs are found
828 // (or less);
829 while( Pos_Ende < MaxTileNum-1 && iPosCount < m_maxTiles-2)
831 for( short Pos = Pos_Ende+1; Pos < MaxTileNum; Pos++)
833 if( isMatchingTile(PosTable[Pos], PosTable[Pos_Ende]) )
835 if (iPosCount < m_maxTiles-2) {
836 PosTable[iPosCount++] = PosTable[Pos_Ende];
837 PosTable[iPosCount++] = PosTable[Pos];
841 Pos_Ende++;
844 if( iPosCount>=2 )
846 random.setSeed(0); // WABA: Why is the seed reset?
847 short Pos = random.getLong(iPosCount) & -2; // Gerader Wert
848 posA = PosTable[Pos];
849 posB = PosTable[Pos+1];
851 return( true );
853 else
854 return( false );
857 int GameData::moveCount( )
859 short Pos_Ende = MaxTileNum; // end of PosTable
861 for( short E=0; E< m_depth; E++ )
863 for( short Y=0; Y< m_height-1; Y++ )
865 for( short X=0; X< m_width-1; X++ )
867 if( MaskData(E,Y,X) != (UCHAR) '1' )
868 continue;
869 if( ! BoardData(E,Y,X) )
870 continue;
871 if( E < m_depth-1 )
873 if( BoardData(E+1,Y,X) || BoardData(E+1,Y+1,X) ||
874 BoardData(E+1,Y,X+1) || BoardData(E+1,Y+1,X+1) )
875 continue;
877 if( X< m_width-2 && (BoardData(E,Y,X-1) || BoardData(E,Y+1,X-1)) &&
878 (BoardData(E,Y,X+2) || BoardData(E,Y+1,X+2)) )
879 continue;
881 Pos_Ende--;
882 PosTable[Pos_Ende].e = E;
883 PosTable[Pos_Ende].y = Y;
884 PosTable[Pos_Ende].x = X;
885 PosTable[Pos_Ende].f = BoardData(E,Y,X);
891 short iPosCount = 0; // store number of pairs found
893 while( Pos_Ende < MaxTileNum-1 && iPosCount < m_maxTiles-2)
895 for( short Pos = Pos_Ende+1; Pos < MaxTileNum; Pos++)
897 if( isMatchingTile(PosTable[Pos], PosTable[Pos_Ende]) )
899 if (iPosCount < m_maxTiles-2) {
900 PosTable[iPosCount++] = PosTable[Pos_Ende];
901 PosTable[iPosCount++] = PosTable[Pos];
905 Pos_Ende++;
908 return iPosCount/2;
914 // ---------------------------------------------------------
915 short GameData::findAllMatchingTiles( POSITION& posA )
917 short Pos = 0;
919 for( short E=0; E< m_depth; E++ )
921 for( short Y=0; Y< m_height-1; Y++ )
923 for( short X=0; X< m_width-1; X++ )
925 if( MaskData(E,Y,X) != (UCHAR) '1' )
926 continue;
927 if( ! BoardData(E,Y,X) )
928 continue;
929 if( E < m_depth-1 )
931 if( BoardData(E+1,Y,X) || BoardData(E+1,Y+1,X) ||
932 BoardData(E+1,Y,X+1) || BoardData(E+1,Y+1,X+1) )
933 continue;
935 if( X< m_width-2 && (BoardData(E,Y,X-1) || BoardData(E,Y+1,X-1)) &&
936 (BoardData(E,Y,X+2) || BoardData(E,Y+1,X+2)) )
937 continue;
939 PosTable[Pos].e = E;
940 PosTable[Pos].y = Y;
941 PosTable[Pos].x = X;
942 PosTable[Pos].f = BoardData(E,Y,X);
944 if( isMatchingTile(posA, PosTable[Pos]) )
945 Pos++;
949 return Pos;
952 bool GameData::loadFromStream(QDataStream & in)
954 in >> Board;
955 in >> Mask;
956 in >> Highlight;
957 in >> allow_undo;
958 in >> allow_redo;
959 in >> TileNum;
960 in >> MaxTileNum;
962 //Read list count
963 in >> m_maxTiles;
965 //Reconstruct the MoveList
966 for (int i = 0; i < m_maxTiles; ++i) {
967 POSITION thispos;
968 in >> thispos.e;
969 in >> thispos.y;
970 in >> thispos.x;
971 in >> thispos.f;
972 setMoveListData( i, thispos);
974 return true;
977 bool GameData::saveToStream(QDataStream & out)
979 out << Board;
980 out << Mask;
981 out << Highlight;
982 out << allow_undo;
983 out << allow_redo;
984 out << TileNum;
985 out << MaxTileNum;
986 //write the size of our lists
987 out << m_maxTiles;
988 //and then write all position components for the MoveList
989 for (int i = 0; i < m_maxTiles; ++i) {
990 POSITION thispos = MoveList.at(i);
991 out << (quint16) thispos.e;
992 out << (quint16) thispos.y;
993 out << (quint16) thispos.x;
994 out << (quint16) thispos.f;
997 return true;
1000 void GameData::shuffle() {
1001 int count = 0;
1002 // copy positions and faces of the remaining tiles into
1003 // the pos table
1004 for (int e=0; e<m_depth; e++) {
1005 for (int y=0; y<m_height; y++) {
1006 for (int x=0; x<m_width; x++) {
1007 if (BoardData(e,y,x) && MaskData(e,y,x) == '1') {
1008 PosTable[count].e = e;
1009 PosTable[count].y = y;
1010 PosTable[count].x = x;
1011 PosTable[count].f = BoardData(e,y,x);
1012 count++;
1020 // now lets randomise the faces, selecting 400 pairs at random and
1021 // swapping the faces.
1022 for (int ran=0; ran < 400; ran++) {
1023 int pos1 = random.getLong(count);
1024 int pos2 = random.getLong(count);
1025 if (pos1 == pos2)
1026 continue;
1027 BYTE f = PosTable[pos1].f;
1028 PosTable[pos1].f = PosTable[pos2].f;
1029 PosTable[pos2].f = f;
1032 // put the rearranged tiles back.
1033 for (int p=0; p<count; p++)
1034 putTile(PosTable[p]);