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.
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";
95 void GameData::setMoveListData(short i
, POSITION
& value
){
96 if ((i
>=MoveList
.size()) || (i
<0)) return ;
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() {
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
;
139 positionDepends
[i
].lhs_dep
[0] = tileAt(x
-1, y
, z
);
140 positionDepends
[i
].lhs_dep
[1] = tileAt(x
-1, y
+1, z
);
143 if (positionDepends
[i
].lhs_dep
[1] == positionDepends
[i
].lhs_dep
[0]) {
144 positionDepends
[i
].lhs_dep
[1] = -1;
148 positionDepends
[i
].rhs_dep
[0] = tileAt(x
+2, y
, z
);
149 positionDepends
[i
].rhs_dep
[1] = tileAt(x
+2, y
+1, z
);
152 if (positionDepends
[i
].rhs_dep
[1] == positionDepends
[i
].rhs_dep
[0]) {
153 positionDepends
[i
].rhs_dep
[1] = -1;
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);
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);
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)) {
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;
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
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;
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.
270 // If this is the first half of a pair, there is no previous
271 // position for the pair.
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
291 placeTile(position
, tilePair
[i
% 144]);
293 // Remember the position
294 lastPosition
= position
;
297 // The game is solvable.
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
305 bool GameData::onlyFreeInLine(int position
) {
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 */
317 nextLeft
[lin
++] = position
;
320 if (positionDepends
[w
].free
|| positionDepends
[w
].filled
) {
323 if ((i
= positionDepends
[w
].lhs_dep
[0]) != -1) {
327 if ((i
= positionDepends
[w
].lhs_dep
[1]) != -1 && i0
!= i
) {
333 /* Check right, starting at position */
336 nextRight
[rin
++] = position
;
338 w
= nextRight
[out
++];
339 if (positionDepends
[w
].free
|| positionDepends
[w
].filled
) {
342 if ((i
= positionDepends
[w
].rhs_dep
[0]) != -1) {
343 nextRight
[rin
++] = i
;
346 if ((i
= positionDepends
[w
].rhs_dep
[1]) != -1 && i0
!= i
) {
347 nextRight
[rin
++] = i
;
352 // Here, the position can be marked "free"
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.
367 position
= random
.getLong(numTilesToGenerate
);
368 if (cnt
++ > (numTilesToGenerate
*numTilesToGenerate
)) {
371 } while (!positionDepends
[position
].free
);
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
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
]);
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".
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.
439 for (int i
= 0; i
< 4; i
++) {
440 if ((depend
= positionDepends
[position
].place_dep
[i
]) != -1) {
441 if (!positionDepends
[depend
].filled
) {
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;
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
464 for (int i
= 0; i
< 2; i
++) {
465 if ((depend
= positionDepends
[position
].lhs_dep
[i
]) != -1) {
466 if (!positionDepends
[depend
].filled
) {
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
483 for (int i
= 0; i
< 2; i
++) {
484 if ((depend
= positionDepends
[position
].rhs_dep
[i
]) != -1) {
485 if (!positionDepends
[depend
].filled
) {
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() {
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
;
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
;
545 for (int tile
=0; tile
<numTilesToGenerate
; tile
+=2) {
550 p2
= p1
= random
.getLong(remaining
-2);
553 p2
= random
.getLong(remaining
-2);
560 if ((tilePositions
[p1
].y
== tilePositions
[p2
].y
) &&
561 (tilePositions
[p1
].e
== tilePositions
[p2
].e
)) {
562 // skip if on same y line
573 a
= tilePositions
[p1
];
574 b
= tilePositions
[p2
];
575 tilePositions
[p1
] = tilePositions
[remaining
- 1];
576 tilePositions
[p2
] = tilePositions
[remaining
- 2];
584 TileNum
= MaxTileNum
;
588 void GameData::getFaces(POSITION
&a
, POSITION
&b
) {
589 a
.f
= tilePair
[tilesUsed
];
590 b
.f
= tilePair
[tilesUsed
+1];
593 if (tilesUsed
>= 144) {
598 void GameData::randomiseFaces() {
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
624 for (int r
=0; r
<200; r
++) {
629 to
= random
.getLong(144);
635 UCHAR tmp
= tilePair
[at
];
636 tilePair
[at
] = tilePair
[to
];
638 tmp
= tilePair
[at
+1];
639 tilePair
[at
+1] = tilePair
[to
+1];
640 tilePair
[to
+1] = tmp
;
648 tilesAllocated
= numAlloced
;
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
) ) )
687 || ( isFlower( FA
) && isFlower( FB
) )
688 || ( isSeason( FA
) && isSeason( FB
) ) )
694 // ---------------------------------------------------------
695 void GameData::setRemovedTilePair(POSITION
&a
, POSITION
&b
) {
698 removedFlower
[a
.f
-TILE_FLOWER
]++;
699 removedFlower
[b
.f
-TILE_FLOWER
]++;
704 removedSeason
[a
.f
-TILE_SEASON
]++;
705 removedSeason
[b
.f
-TILE_SEASON
]++;
708 if (isCharacter(a
.f
)) {
709 removedCharacter
[a
.f
- TILE_CHARACTER
]+=2;
714 removedBamboo
[a
.f
- TILE_BAMBOO
]+=2;
718 removedRod
[a
.f
- TILE_ROD
]+=2;
722 removedDragon
[a
.f
- TILE_DRAGON
]+=2;
726 removedWind
[a
.f
- TILE_WIND
]+=2;
731 // ---------------------------------------------------------
732 void GameData::clearRemovedTilePair(POSITION
&a
, POSITION
&b
) {
735 removedFlower
[a
.f
-TILE_FLOWER
]--;
736 removedFlower
[b
.f
-TILE_FLOWER
]--;
741 removedSeason
[a
.f
-TILE_SEASON
]--;
742 removedSeason
[b
.f
-TILE_SEASON
]--;
745 if (isCharacter(a
.f
)) {
746 removedCharacter
[a
.f
- TILE_CHARACTER
]-=2;
751 removedBamboo
[a
.f
- TILE_BAMBOO
]-=2;
755 removedRod
[a
.f
- TILE_ROD
]-=2;
759 removedDragon
[a
.f
- TILE_DRAGON
]-=2;
763 removedWind
[a
.f
- TILE_WIND
]-=2;
769 // ---------------------------------------------------------
770 void GameData::initialiseRemovedTiles() {
771 for (int pos
=0; pos
<9; pos
++) {
772 removedCharacter
[pos
]=0;
773 removedBamboo
[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' )
798 if( ! BoardData(E
,Y
,X
) )
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) )
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)) )
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
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
];
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];
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' )
869 if( ! BoardData(E
,Y
,X
) )
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) )
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)) )
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
];
914 // ---------------------------------------------------------
915 short GameData::findAllMatchingTiles( POSITION
& posA
)
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' )
927 if( ! BoardData(E
,Y
,X
) )
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) )
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)) )
942 PosTable
[Pos
].f
= BoardData(E
,Y
,X
);
944 if( isMatchingTile(posA
, PosTable
[Pos
]) )
952 bool GameData::loadFromStream(QDataStream
& in
)
965 //Reconstruct the MoveList
966 for (int i
= 0; i
< m_maxTiles
; ++i
) {
972 setMoveListData( i
, thispos
);
977 bool GameData::saveToStream(QDataStream
& out
)
986 //write the size of our lists
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
;
1000 void GameData::shuffle() {
1002 // copy positions and faces of the remaining tiles into
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
);
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
);
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
]);