update version
[colorflood] / colorflood / src / field.cpp
index d47250a..139aab3 100644 (file)
   GNU General Public License for more details.
 */
 
-#include <QtGui>
+#include <QMetaType>
+#include <QDataStream>
+#include <QSettings>
+#include <QTime>
+#include <QPainter>
+#include <QPaintEvent>
+#include <QMessageBox>
 #include "field.hpp"
+#include "colorscheme.hpp"
 
 static const int fieldWidth = 420;
 
-const int Field::rects[Field::NUM_SIZES] = { 14, 21, 28 };
-const int Field::turns[Field::NUM_SIZES] = { 25, 35, 50 };
+const int Field::numRects[Field::NUM_SIZES] = { 14, 21, 28 };
+const int Field::numTurns[Field::NUM_SIZES] = { 25, 35, 50 };
 
-Field::Field (QWidget *parent)
-    : QWidget (parent)
+// we declare out QVector<FieldRect> metatype
+// and stream operators to save whole field to settings
+Q_DECLARE_METATYPE(Field::RectVector);
+
+static QDataStream &operator<< (QDataStream &out, const Field::RectVector &rv)
 {
-    // FIXME -- restore saved state
+    for (QVector<Field::FieldRect>::const_iterator rect = rv.begin();
+         rect != rv.end();
+         rect++)
+    {
+        out << (*rect).brush;
+        out << (*rect).flood;
+    }
+
+    return out;
+}
+
+static QDataStream &operator>> (QDataStream &in, Field::RectVector &rv)
+{
+    Field::FieldRect r;
+
+    rv.clear();
+
+    for (; !in.atEnd() ;)
+    {
+        in >> r.brush >> r.flood;
+        rv << r;
+    }
+
+    rv.pop_back();
+
+    return in;
 }
 
-Field::Field (QWidget *parent, const QVector<QBrush> &brushes, FieldSize size)
-    : QWidget (parent)
+Field::Field (QWidget *parent, int *turns)
+    : QWidget (parent),
+      finished(false)
 {
-    init(brushes, size);
+    Q_ASSERT(parent);
+
+    setFixedSize(fieldWidth, fieldWidth);
+
+    // restore field size and field itself from settings
+
+    qRegisterMetaType<RectVector>("Field::RectVector");
+    qRegisterMetaTypeStreamOperators<RectVector>("Field::RectVector");
+
+    QSettings settings;
+
+    int size = settings.value("field/size", SIZE_SMALL).toInt();
+
+    if (size < SIZE_SMALL || size >= NUM_SIZES)
+        size = SIZE_SMALL;
+
+    this->size = (FieldSize)size;
+
+    if (settings.contains("field/data"))
+        data = settings.value("field/data").value<RectVector>();
+
+    this->turns = settings.value("field/turns", 0).toInt();
+
+    if (data.size() != numRects[size] * numRects[size])
+        randomize();
+
+    *turns = this->turns;
 }
 
 Field::~Field ()
 {
-    // FIXME -- save state
+    if (!finished)
+    {
+        QSettings settings;
+
+        settings.setValue("field/size", size);
+
+        QVariant v;
+        v.setValue(data);
+        settings.setValue("field/data", v);
+
+        settings.setValue("field/turns", turns);
+    }
 }
 
 Field::FieldSize Field::getSize () const
@@ -41,51 +114,92 @@ Field::FieldSize Field::getSize () const
     return size;
 }
 
-/*
-=================
-randomize
-=================
-*/
+void Field::setSize (int size)
+{
+    Q_ASSERT(size >= 0 && size < NUM_SIZES);
+
+    if (this->size == size)
+        return;
+
+    this->size = (FieldSize)size;
+    randomize();
+}
+
 void Field::randomize ()
 {
-    Field::FieldRect rect;
+    FieldRect rect;
+
+    rect.brush = 0;
     rect.flood = false;
 
     data.clear();
-    data = QVector<FieldRect> (rects[size] * rects[size], rect);
+    data = RectVector(numRects[size] * numRects[size], rect);
 
     qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime()));
 
-    for (QVector<Field::FieldRect>::iterator rect = data.begin();
+    int numBrushes = ColorScheme::instance().getScheme().size();
+
+    for (QVector<FieldRect>::iterator rect = data.begin();
          rect != data.end();
          rect++)
     {
-        (*rect).brush = qrand() % brushes.size();
+        (*rect).brush = qrand() % numBrushes;
     }
+
+    turns = 0;
+    finished = false;
+    emit turnsChanged(turns);
+
+    // flood from top-left
+    data[0].flood = true;
+    floodNeighbours(data[0].brush, 0, 0);
+
+    update();
 }
 
 int Field::getNumRectsOfSize (FieldSize size)
 {
-    return rects[size];
+    return numRects[size];
 }
 
 int Field::getNumTurnsOfSize (FieldSize size)
 {
-    return turns[size];
+    return numTurns[size];
 }
 
-void Field::init (const QVector<QBrush> &brushes, FieldSize size)
+int Field::getRectSize (FieldSize size)
 {
-    this->size = size;
-    this->brushes = brushes;
+    return fieldWidth / numRects[size];
+}
 
-    setFixedSize(fieldWidth, fieldWidth);
-    randomize();
+void Field::tryFloodRecurse (quint8 brush, int x, int y)
+{
+    FieldRect &rect = data[x + y*numRects[size]];
+
+    if (!rect.flood && rect.brush == brush)
+    {
+        rect.flood = true;
+        floodNeighbours(brush, x, y);
+    }
 }
 
-int Field::getRectSize (FieldSize size)
+void Field::floodNeighbours (quint8 brush, int x, int y)
 {
-    return fieldWidth / rects[size];
+    int s = numRects[size];
+
+    data[x + y*s].brush = brush;
+
+    if (x > 0)
+        tryFloodRecurse(brush, x - 1, y);
+
+    if (y > 0)
+        tryFloodRecurse(brush, x, y - 1);
+
+    if (x < s - 1)
+        tryFloodRecurse(brush, x + 1, y);
+        
+    if (y < s - 1)
+        tryFloodRecurse(brush, x, y + 1);
 }
 
 void Field::paintEvent (QPaintEvent *event)
@@ -95,18 +209,84 @@ void Field::paintEvent (QPaintEvent *event)
 
     QRect rect = QRect(0, 0, getRectSize(size), getRectSize(size));
 
-    for (int y = 0; y < rects[size] ;y++)
+    const QVector<QBrush> &scheme = ColorScheme::instance().getScheme();
+
+    for (int y = 0; y < numRects[size] ;y++)
     {
-        int n = y * rects[size];
+        int n = y * numRects[size];
 
-        for (int x = 0; x < rects[size] ;x++, n++)
+        for (int x = 0; x < numRects[size] ;x++, n++)
         {
             rect.moveTo(x * rect.width(), y * rect.height());
 
             if (rect.intersects(event->rect()))
-                painter.fillRect(rect, brushes.at(data[n].brush));
+                painter.fillRect(rect, scheme.at(data[n].brush));
         }
     }
 
     painter.end();
 }
+
+void Field::flood (int colorIndex)
+{
+    // don't fill with the same color over and over again
+    if (colorIndex == data[0].brush)
+        return;
+
+    if (finished)
+        return;
+
+    emit turnsChanged(++turns);
+
+    // flood with new color
+    for (int y = 0; y < numRects[size] ;y++)
+    {
+        int n = y * numRects[size];
+
+        for (int x = 0; x < numRects[size] ;x++, n++)
+        {
+            if (data[n].flood)
+                floodNeighbours(colorIndex, x, y);
+        }
+    }
+
+    repaint();
+
+    bool allFlooded = true;
+
+    // check if all field flooded
+    for (QVector<Field::FieldRect>::const_iterator rect = data.begin();
+         rect != data.end();
+         rect++)
+    {
+        if (!(*rect).flood)
+        {
+            allFlooded = false;
+            break;
+        }
+    }
+
+    QString msg;
+
+    if (allFlooded)
+    {
+        finished = true;
+        /*: win message */
+        msg = tr("You won!");
+    }
+    else if (getNumTurnsOfSize(size) == turns)
+    {
+        finished = true;
+        /*: fail message */
+        msg = tr("You lost!");
+    }
+
+    if (finished)
+    {
+        QMessageBox box;
+        box.setWindowTitle("Color Flood");
+        box.setText(msg);
+        box.exec();
+        randomize();
+    }
+}