Fixed bug when block can slide over the same one
[vexed] / playfield.cpp
1 #include "playfield.h"
2 #include <stdio.h>
3 PlayField::PlayField(const QString &_title, const QString &_board, const QString &_solution):title(_title),solution(_solution)
4 {
5     for(int w=0;w<PF::FIELD_WIDTH;w++)
6         for(int h=0;h<PF::FIELD_HEIGHT;h++)
7             field[w][h]=PF::CELL_WALL;
8     QStringList rows=_board.split("/");
9     QListIterator<QString> rowI(rows);
10     int h=0;
11     while(rowI.hasNext())
12     {
13         QString row=rowI.next();
14         int w=0;
15         for(int i=0;i<row.size();i++)
16         {
17             char ch=row[i].toAscii();
18             int c=0;
19             while(ch>='0' && ch<='9')
20             {
21                 c=c*10+ch-'0';
22                 ch=row[++i].toAscii();
23                 if(i>=row.size()) break;
24             }
25             w+=c;
26             if(ch=='~')
27             {
28                 field[w++][h]=PF::CELL_EMPTY;
29             }
30             if(ch>='a' && ch <= 'h')
31             {
32                 field[w++][h]=ch-'a'+2;
33             }
34         }
35         h++;
36     }
37 }
38
39 void PlayField::move(int w, int h, int w_new)
40 {
41         if((w_new < 0) ||
42            (w_new >= PF::FIELD_WIDTH) || w == w_new)
43         {
44                 return;
45         }
46         if(get(w,h)==PF::CELL_EMPTY ||
47            get(w,h)==PF::CELL_WALL)
48         {
49                 return;
50         }
51         int d=w>w_new?-1:1;
52
53         Field temp;
54         int movesTemp=moves;
55         copy(field,temp);
56
57         bool moved=false;
58         bool cont;
59
60         int h_below=h+1;
61         do
62         {            
63             cont=moveBlock(w,h,d,0);
64             w+=d;
65             if(cont){
66                 moves++;
67                 moved=true;
68             }//if coordination has changed, inc the moves
69         } while(cont && // still moving
70                 (w!=w_new) && // didn't arrived
71                 ((h_below >= PF::FIELD_HEIGHT) || (get(w,h_below)!=PF::CELL_EMPTY && get(w,h_below) != get(w,h)))  // there is something below
72                 );
73         do
74         {
75             cont=false;
76             cont|=checkGlobalFall();
77             cont|=checkTouch();
78         } while(cont);
79         if(moved)
80         {
81             if(totalUndo<PF::MAX_UNDO) totalUndo++;
82             copy(temp,undos[currentUndo]);
83             undoMade[currentUndo]=movesTemp;
84             currentUndo=(currentUndo + 1) % PF::MAX_UNDO;
85         }
86 }
87
88 bool PlayField::moveBlock(int w, int h, int dw, int dh)
89 {
90     int wn=w+dw;
91     int hn=h+dh;
92
93     if(wn<0 || wn >= PF::FIELD_WIDTH || hn < 0 || hn >= PF::FIELD_HEIGHT)
94         return false;
95
96     if(get(wn,hn)!=PF::CELL_EMPTY)
97         return false;
98     set(wn, hn, get(w,h));
99     set(w,h,PF::CELL_EMPTY);
100     emit cellMoved(w,h,wn,hn);
101     return true;
102 }
103 bool PlayField::checkGlobalFall()
104 {
105     bool fall;
106     bool fallen=false;
107     do
108     {
109         fall=false;
110         for(int w=0;w<PF::FIELD_WIDTH;w++)
111             for(int h=0;h<PF::FIELD_HEIGHT;h++)
112                 if(checkFall(w,h))
113                 {
114                     if(moveBlock(w,h,0,1))
115                     {
116                         fall=true;
117                         fallen=true;
118                     }
119                 }
120     } while(fall);
121     return fallen;
122 }
123
124 bool PlayField::checkFall(int w, int h)
125 {
126     int cell=get(w,h);
127     return (cell!=PF::CELL_EMPTY) &&
128             (cell!=PF::CELL_WALL) &&
129             (h!=(PF::FIELD_HEIGHT-1)) &&
130             (get(w,h+1)==PF::CELL_EMPTY);
131 }
132 bool PlayField::checkTouch()
133 {
134     int toHide[PF::FIELD_WIDTH][PF::FIELD_HEIGHT];
135
136     for (int w = 0; w < PF::FIELD_WIDTH; w++) {
137             for (int h = 0; h < PF::FIELD_HEIGHT; h++) {
138                     toHide[w][h] = PF::CELL_EMPTY;
139                     if ((get(w,h) != PF::CELL_EMPTY) && (get(w,h)!=PF::CELL_WALL)) {
140                             if (h != 0) {
141                                     toHide[w][h] |= (get(w,h-1) == get(w,h));
142                             }
143                             if (h != PF::FIELD_HEIGHT - 1) {
144                                     toHide[w][h] |= (get(w,h+1) == get(w,h));
145                             }
146                             if (w != 0) {
147                                     toHide[w][h] |= (get(w-1,h) == get(w,h));
148                             }
149                             if (w != PF::FIELD_WIDTH - 1) {
150                                     toHide[w][h] |= (get(w+1,h) == get(w,h));
151                             }
152                     }
153             }
154     }
155
156     bool touched=false;
157     for (int w = 0; w < PF::FIELD_WIDTH; w++) {
158             for (int h = 0; h < PF::FIELD_HEIGHT; h++) {
159                     if(toHide[w][h])
160                     {
161                             set(w,h,PF::CELL_EMPTY);
162                             emit cellGone(w,h);
163                             touched=true;
164                     }
165             }
166     }
167     return touched;
168 }
169
170 bool PlayField::checkSolved()
171 {
172         for(int w=0;w<PF::FIELD_WIDTH-1;w++)
173                 for(int h=0;h<PF::FIELD_HEIGHT-1;h++)
174                 {
175                         int cell=get(w,h);
176                         if((cell!=PF::CELL_EMPTY) && (cell!=PF::CELL_WALL)) return false;
177                 }
178         return true;
179 }
180
181 void PlayField::undo()
182 {
183     if(totalUndo > 0)
184     {
185         totalUndo--;
186         currentUndo=(currentUndo - 1 + PF::MAX_UNDO) % PF::MAX_UNDO;
187         copy(undos[currentUndo],field);
188         moves=undoMade[currentUndo];
189     }
190 }