fix warning
[colorflood] / colorflood / src / field.cpp
1 /*
2   Copyright 2010 Serge Ziryukin <ftrvxmtrx@gmail.com>
3
4   This program 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; version 2 of the License.
7
8   This program is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   GNU General Public License for more details.
12 */
13
14 #include <QMetaType>
15 #include <QDataStream>
16 #include <QSettings>
17 #include <QTime>
18 #include <QPainter>
19 #include <QPaintEvent>
20 #include <QMessageBox>
21 #include "field.hpp"
22 #include "colorscheme.hpp"
23
24 static const int fieldWidth = 420;
25
26 const int Field::numRects[Field::NUM_SIZES] = { 14, 21, 28 };
27 const int Field::numTurns[Field::NUM_SIZES] = { 25, 35, 50 };
28
29 // we declare out QVector<FieldRect> metatype
30 // and stream operators to save whole field to settings
31 Q_DECLARE_METATYPE(Field::RectVector);
32
33 static QDataStream &operator<< (QDataStream &out, const Field::RectVector &rv)
34 {
35     for (QVector<Field::FieldRect>::const_iterator rect = rv.begin();
36          rect != rv.end();
37          rect++)
38     {
39         out << (*rect).brush;
40         out << (*rect).flood;
41     }
42
43     return out;
44 }
45
46 static QDataStream &operator>> (QDataStream &in, Field::RectVector &rv)
47 {
48     Field::FieldRect r;
49
50     rv.clear();
51
52     for (; !in.atEnd() ;)
53     {
54         in >> r.brush >> r.flood;
55         rv << r;
56     }
57
58     rv.pop_back();
59
60     return in;
61 }
62
63 Field::Field (QWidget *parent, int *turns)
64     : QWidget (parent),
65       finished(false)
66 {
67     Q_ASSERT(parent);
68
69     setFixedSize(fieldWidth, fieldWidth);
70
71     // restore field size and field itself from settings
72
73     qRegisterMetaType<RectVector>("Field::RectVector");
74     qRegisterMetaTypeStreamOperators<RectVector>("Field::RectVector");
75
76     QSettings settings;
77
78     int size = settings.value("field/size", SIZE_SMALL).toInt();
79
80     if (size < SIZE_SMALL || size >= NUM_SIZES)
81         size = SIZE_SMALL;
82
83     this->size = (FieldSize)size;
84
85     if (settings.contains("field/data"))
86         data = settings.value("field/data").value<RectVector>();
87
88     this->turns = settings.value("field/turns", 0).toInt();
89
90     if (data.size() != numRects[size] * numRects[size])
91         randomize();
92
93     *turns = this->turns;
94 }
95
96 Field::~Field ()
97 {
98     if (!finished)
99     {
100         QSettings settings;
101
102         settings.setValue("field/size", size);
103
104         QVariant v;
105         v.setValue(data);
106         settings.setValue("field/data", v);
107
108         settings.setValue("field/turns", turns);
109     }
110 }
111
112 Field::FieldSize Field::getSize () const
113 {
114     return size;
115 }
116
117 void Field::setSize (int size)
118 {
119     Q_ASSERT(size >= 0 && size < NUM_SIZES);
120
121     if (this->size == size)
122         return;
123
124     this->size = (FieldSize)size;
125     randomize();
126 }
127
128 void Field::randomize ()
129 {
130     FieldRect rect;
131
132     rect.brush = 0;
133     rect.flood = false;
134
135     data.clear();
136     data = RectVector(numRects[size] * numRects[size], rect);
137
138     qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime()));
139
140     int numBrushes = ColorScheme::instance().getScheme().size();
141
142     for (QVector<FieldRect>::iterator rect = data.begin();
143          rect != data.end();
144          rect++)
145     {
146         (*rect).brush = qrand() % numBrushes;
147     }
148
149     turns = 0;
150     finished = false;
151     emit turnsChanged(turns);
152
153     // flood from top-left
154     data[0].flood = true;
155     floodNeighbours(data[0].brush, 0, 0);
156
157     update();
158 }
159
160 int Field::getNumRectsOfSize (FieldSize size)
161 {
162     return numRects[size];
163 }
164
165 int Field::getNumTurnsOfSize (FieldSize size)
166 {
167     return numTurns[size];
168 }
169
170 int Field::getRectSize (FieldSize size)
171 {
172     return fieldWidth / numRects[size];
173 }
174
175 void Field::tryFloodRecurse (quint8 brush, int x, int y)
176 {
177     FieldRect &rect = data[x + y*numRects[size]];
178
179     if (!rect.flood && rect.brush == brush)
180     {
181         rect.flood = true;
182         floodNeighbours(brush, x, y);
183     }
184 }
185
186 void Field::floodNeighbours (quint8 brush, int x, int y)
187 {
188     int s = numRects[size];
189
190     data[x + y*s].brush = brush;
191
192     if (x > 0)
193         tryFloodRecurse(brush, x - 1, y);
194
195     if (y > 0)
196         tryFloodRecurse(brush, x, y - 1);
197
198     if (x < s - 1)
199         tryFloodRecurse(brush, x + 1, y);
200         
201     if (y < s - 1)
202         tryFloodRecurse(brush, x, y + 1);
203 }
204
205 void Field::paintEvent (QPaintEvent *event)
206 {
207     QPainter painter;
208     painter.begin(this);
209
210     QRect rect = QRect(0, 0, getRectSize(size), getRectSize(size));
211
212     const QVector<QBrush> &scheme = ColorScheme::instance().getScheme();
213
214     for (int y = 0; y < numRects[size] ;y++)
215     {
216         int n = y * numRects[size];
217
218         for (int x = 0; x < numRects[size] ;x++, n++)
219         {
220             rect.moveTo(x * rect.width(), y * rect.height());
221
222             if (rect.intersects(event->rect()))
223                 painter.fillRect(rect, scheme.at(data[n].brush));
224         }
225     }
226
227     painter.end();
228 }
229
230 void Field::flood (int colorIndex)
231 {
232     // don't fill with the same color over and over again
233     if (colorIndex == data[0].brush)
234         return;
235
236     if (finished)
237         return;
238
239     emit turnsChanged(++turns);
240
241     // flood with new color
242     for (int y = 0; y < numRects[size] ;y++)
243     {
244         int n = y * numRects[size];
245
246         for (int x = 0; x < numRects[size] ;x++, n++)
247         {
248             if (data[n].flood)
249                 floodNeighbours(colorIndex, x, y);
250         }
251     }
252
253     repaint();
254
255     bool allFlooded = true;
256
257     // check if all field flooded
258     for (QVector<Field::FieldRect>::const_iterator rect = data.begin();
259          rect != data.end();
260          rect++)
261     {
262         if (!(*rect).flood)
263         {
264             allFlooded = false;
265             break;
266         }
267     }
268
269     QString msg;
270
271     if (allFlooded)
272     {
273         finished = true;
274         /*: win message */
275         msg = tr("You won!");
276     }
277     else if (getNumTurnsOfSize(size) == turns)
278     {
279         finished = true;
280         /*: fail message */
281         msg = tr("You lost!");
282     }
283
284     if (finished)
285     {
286         QMessageBox box;
287         box.setWindowTitle("Color Flood");
288         box.setText(msg);
289         box.exec();
290         randomize();
291     }
292 }