Logo Search packages:      
Sourcecode: hexalate version File versions  Download package

puzzle.cpp

/***********************************************************************
 *
 * Copyright (C) 2009 Graeme Gott <graeme@gottcode.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 ***********************************************************************/

#include "puzzle.h"

#include "piece.h"

#include <QGraphicsPathItem>
#include <QGraphicsTextItem>
#include <QMessageBox>
#include <QPainterPath>
#include <QSettings>

#include <algorithm>

/*****************************************************************************/

static int match(int offset) {
      if (offset > 2) {
            return offset - 3;
      } else {
            return offset + 3;
      }
}

static QList<QPointF> positions = QList<QPointF>()
      << QPointF(110, 110)
      << QPointF(110, 10)
      << QPointF(196.6, 60)
      << QPointF(196.6, 160)
      << QPointF(110, 210)
      << QPointF(23.4, 160)
      << QPointF(23.4, 60);

/*****************************************************************************/

Puzzle::Puzzle(QObject* parent)
: QGraphicsScene(0, 0, 320, 320, parent),
  m_done(false) {
      if (!QSettings().contains("Current/Piece1") || !load()) {
            generate();
      }
}

/*****************************************************************************/

Puzzle::~Puzzle() {
      QSettings().remove("Current");
      if (!m_done) {
            save();
      }
}

/*****************************************************************************/

void Puzzle::generate() {
      // Remove old game
      clear();
      m_pieces.clear();
      m_done = false;

      // Set random seed
      srand(time(0));

      // Add board background
      createBackground();

      // Initialize pieces
      for (int i = 0; i < 7; ++i) {
            m_pieces.append(new Piece(this));
      }

      // Create first piece
      QList<int> colors = m_pieces.first()->colors();
      std::random_shuffle(colors.begin(), colors.end());
      for (int i = 0; i < 6; ++i) {
            int color = colors.at(i);
            m_pieces.first()->setConnector(i, color);
            m_pieces.at(i + 1)->setConnector(match(i), color);
      }

      // Create ring of pieces
      for (int i = 1; i < 7; ++i) {
            int connector = match(i - 1);
            int offset = (connector > 0) ? connector - 1 : 5;
            int next = (i < 6) ? (i + 1) : 1;
            QList<int> colors1 = m_pieces.at(i)->colors();
            QList<int> colors2 = m_pieces.at(next)->colors();
            colors = colors1 + colors2;
            colors.erase(std::set_intersection(colors1.begin(), colors1.end(), colors2.begin(), colors2.end(), colors.begin()), colors.end());
            int color = colors.at(rand() % colors.count());
            m_pieces.at(i)->setConnector(offset, color);
            m_pieces.at(next)->setConnector(match(offset), color);
      }

      // Randomly set remaining connectors
      for (int i = 1; i < 7; ++i) {
            Piece* piece = m_pieces.at(i);
            colors = piece->colors();
            std::random_shuffle(colors.begin(), colors.end());
            for (int j = 0; j < 6; ++j) {
                  if (piece->connector(j) == -1) {
                        piece->setConnector(j, colors.takeFirst());
                  }
            }
      }

      // Randomize board
      std::random_shuffle(m_pieces.begin(), m_pieces.end());
      foreach (Piece* piece, m_pieces) {
            piece->spin();
      }

      // Position pieces
      positionPieces();
}

/*****************************************************************************/

bool Puzzle::isDone() {
      if (!m_done) {
            // Check center
            for (int i = 0; i < 6; ++i) {
                  if (m_pieces.first()->connector(i) != m_pieces.at(i + 1)->connector(match(i))) {
                        return false;
                  }
            }

            // Check ring
            for (int i = 1; i < 7; ++i) {
                  int connector = match(i - 1);
                  int offset = (connector > 0) ? connector - 1 : 5;
                  int next = (i < 6) ? (i + 1) : 1;
                  if (m_pieces.at(i)->connector(offset) != m_pieces.at(next)->connector(match(offset))) {
                        return false;
                  }
            }

            // Disable interaction with completed board
            for (int i = 0; i < 7; ++i) {
                  m_pieces.at(i)->setFlag(QGraphicsItem::ItemIsMovable, false);
            }

            // Show success message
            QGraphicsTextItem* message = addText(tr("Success"), QFont("Sans", 24));
            QRectF rect = message->boundingRect();
            message->setDefaultTextColor(Qt::white);
            message->setPos(rect.height() * .25, rect.height() * .25);

            QPainterPath path;
            path.addRoundedRect(0, 0, rect.width() + rect.height() * .5, rect.height() * 1.5, 10, 10);
            QGraphicsPathItem* background = addPath(path, Qt::NoPen, QColor(0, 0, 0, 200));
            message->setParentItem(background);
            background->setZValue(1000);
            background->setPos(sceneRect().center() - background->boundingRect().center());

            // Store status for future checks
            m_done = true;
      }
      return true;
}

/*****************************************************************************/

void Puzzle::swapPieces(Piece* piece1, Piece* piece2) {
      int pos1 = m_pieces.indexOf(piece1);
      int pos2 = m_pieces.indexOf(piece2);
      Q_ASSERT(pos1 != -1 && pos2 != -1);
      m_pieces.swap(pos1, pos2);
}

/*****************************************************************************/

bool Puzzle::load() {
      // Add board background
      createBackground();

      // Create pieces
      QSettings settings;
      bool success = true;
      for (int i = 0; i < 7; ++i) {
            Piece* piece = new Piece(this);
            success &= piece->fromString(settings.value(QString("Current/Piece%1").arg(i+1)).toString());
            m_pieces.append(piece);
      }
      if (!success) {
            QMessageBox::information(0, tr("Sorry"), tr("Unable to load previous game. Starting a new game."));
            return false;
      }

      // Position pieces
      positionPieces();

      return true;
}

/*****************************************************************************/

void Puzzle::save() {
      QSettings settings;
      for (int i = 0; i < 7; ++i) {
            settings.setValue(QString("Current/Piece%1").arg(i+1), m_pieces[i]->toString());
      }
}

/*****************************************************************************/

void Puzzle::createBackground() {
      QGraphicsItem* background = addEllipse(10, 10, 300, 300, QPen(Qt::darkGray, 2), Qt::gray);
      foreach (const QPointF& position, positions) {
            QGraphicsItem* item = addEllipse(0, 0, 70, 70, Qt::NoPen, Qt::darkGray);
            item->setPos(position + QPointF(15,15));
            item->setParentItem(background);
      }
}

/*****************************************************************************/

void Puzzle::positionPieces() {
      for (int i = 0; i < 7; ++i) {
            m_pieces[i]->setPosition(positions[i]);
      }
}

/*****************************************************************************/

Generated by  Doxygen 1.6.0   Back to index