Go back to Richel Bilderbeek's homepage.

Go back to Richel Bilderbeek's tools page.

 

 

 

 

 

(C++ tool) MazeCreator

 

MazeCreator is a C++ tool to demonstrate my simple maze generation algorithm.

 

 

 

 

 

 

MazeCreator history

 

 

 

 

 

 

MazeCreator source code

 

Operating system: Ubuntu 10.04 LTS Lucid Lynx

IDE: Qt Creator 2.0.0

Project type: Qt4 GUI Application

Compiler: G++ 4.4.1

Libraries used:

 

 

 

 

 

Qt project file

 

#-------------------------------------------------
#
# Project created by QtCreator 2010-07-24T21:37:56
#
#-------------------------------------------------
QT += core gui
TARGET = ToolMazeCreator
TEMPLATE = app
SOURCES += main.cpp\
maindialog.cpp
HEADERS += maindialog.h
FORMS += maindialog.ui
RESOURCES += \
resources.qrc

 

 

 

 

 

Source code

 

 

 

 

 

main.cpp

 

#include <QtGui/QApplication>
#include "maindialog.h"

int main(int argc, char *argv[])
{
  QApplication a(argc, argv);
  MainDialog w;
  w.show();
  return a.exec();
}

 

 

 

 

 

maindialog.h

 

#ifndef MAINDIALOG_H
#define MAINDIALOG_H

#include <boost/shared_ptr.hpp>
#include <QDialog>
struct QGraphicsPixmapItem;
struct QGraphicsScene;
struct QTimer;

namespace Ui {
  class MainDialog;
}

class MainDialog : public QDialog
{
  Q_OBJECT

public:
  explicit MainDialog(QWidget *parent = 0);
  ~MainDialog();


protected:
  void changeEvent(QEvent *e);

private:
  Ui::MainDialog *ui;
  boost::shared_ptr<QGraphicsScene> m_scene;
  boost::shared_ptr<QGraphicsPixmapItem> m_background;
  boost::shared_ptr<QTimer> m_timer;

  std::size_t m_maze_sz;
  double m_rotation;


  void resizeEvent(QResizeEvent*);
  void keyPressEvent(QKeyEvent* event);
  void drawMaze();

private slots:
  void onTimer();

};

//From http://www.richelbilderbeek.nl/CppCreateMaze.htm
std::vector<std::vector<int> > CreateMaze(const int size);

#endif // MAINDIALOG_H

 

 

 

 

 

maindialog.cpp

 

#include <cassert>
#include <cmath>
#include <cstdlib>
#include <iostream>

#include <QDesktopWidget>
#include <QGraphicsPixmapItem>
#include <QGraphicsScene>
#include <QKeyEvent>
#include <QTimer>

#include "maindialog.h"
#include "ui_maindialog.h"

MainDialog::MainDialog(QWidget *parent) :
    QDialog(parent,Qt::Window),
    ui(new Ui::MainDialog),
    m_scene(new QGraphicsScene),
    m_background(new QGraphicsPixmapItem),
    m_timer(new QTimer),
    m_maze_sz(7),
    m_rotation(0.0)
{
  ui->setupUi(this);
  ui->graphicsView->setScene(m_scene.get());
  m_scene->addItem(m_background.get());

  this->drawMaze();

  //Set all connections
  QObject::connect(m_timer.get(),SIGNAL(timeout()),
    this,SLOT(onTimer()));

  //Set the rotation timer to work
  m_timer->start(50);

  //Put the dialog to its proper size and spot
  this->setGeometry(0,0,
    static_cast<int>(640),
    static_cast<int>(640));
  const QRect screen = QApplication::desktop()->screenGeometry();
  this->move( screen.center() - this->rect().center() );
}

MainDialog::~MainDialog()
{
  delete ui;
}

void MainDialog::changeEvent(QEvent *e)
{
  QDialog::changeEvent(e);
  switch (e->type()) {
  case QEvent::LanguageChange:
    ui->retranslateUi(this);
    break;
  default:
    break;
  }
}

//Sets the scale of the maze
void MainDialog::resizeEvent(QResizeEvent*)
{
  const double scale
    = 0.9 * std::min(ui->graphicsView->width(),ui->graphicsView->height())
    / (static_cast<double>(this->m_maze_sz) * std::sqrt(2.0));
  m_background->setScale(scale);
}

void MainDialog::keyPressEvent(QKeyEvent* event)
{
  if (event->type() == QEvent::KeyPress)
  {
    std::clog << "Key pressed\n";
    switch (event->key())
    {
      case Qt::Key_Plus:
          std::clog << "Key plus pressed\n";
          m_maze_sz+=4;
          drawMaze();
          resizeEvent(0);
        break;
      case Qt::Key_Minus:
          std::clog << "Key minus pressed\n";
          if (m_maze_sz == 7) return;
          m_maze_sz-=4;
          drawMaze();
          resizeEvent(0);
        break;
    }
  }
}

void MainDialog::drawMaze()
{
  const int size = static_cast<int>(m_maze_sz);
  //Prepare a pixmap of right size
  //and set the rotation origin
  {
    QPixmap pixmap(size,size);
    m_background->setPixmap(pixmap);
    m_background->setTransformOriginPoint(
      static_cast<double>(size) / 2.0,
      static_cast<double>(size) / 2.0);
  }

  const std::vector<std::vector<int> > maze = CreateMaze(size);
  assert(maze.size() == m_maze_sz);
  assert(maze[0].size() == m_maze_sz);

  QImage i = m_background->pixmap().toImage();

  for (int y=0; y!=size; ++y)
  {
    for (int x=0; x!=size; ++x)
    {
      switch(maze[x][y])
      {
        case 0: //Road
          i.setPixel(QPoint(x,y),QColor(255,255,255).rgb());
          break;
        case 1: //Wall
          i.setPixel(QPoint(x,y),QColor(0,0,0).rgb());
          break;
        case 2: //Unexplorer
          i.setPixel(QPoint(x,y),QColor(255,0,0).rgb());
          break;
        default:
          std::cerr << maze[x][y] << '\n';
          assert(!"Should not get here");
          break;
      }
    }
  }
  m_background->setPixmap(m_background->pixmap().fromImage(i));
}

void MainDialog::onTimer()
{
  m_rotation+=1.0;
  m_background->setRotation(m_rotation);
}
//---------------------------------------------------------------------------
//Creates a maze
// 0 : path
// 1 : wall
//From http://www.richelbilderbeek.nl/CppCreateMaze.htm
std::vector<std::vector<int> > CreateMaze(const int sz)
{
  //Assume correct size dimensions
  assert( sz > 4 && sz % 4 == 3
    && "Size must be 3 + (n * 4) for n > 0");

  //Start with a wall-only maze
  std::vector<std::vector<int> > maze(sz, std::vector<int>(sz,1));

  //Prepare maze, remove paths
  // XXXXXXX 1111111
  // X X X X 1212121
  // XXXXXXX 1111111
  // X XOX X -> 1210121
  // XXXXXXX 1111111
  // X X X X 1212121
  // XXXXXXX 1111111

  //Draw open spaces
  for (int y=1; y<sz; y+=2)
  {
    for (int x=1; x<sz; x+=2)
    {
      maze[y][x] = 2; //2: unexplored
    }
  }

  const int mid = sz/2;

  maze[mid][mid] = 0;

  std::vector<std::pair<int,int> > v;
  v.push_back(std::make_pair(mid,mid));
  while (!v.empty())
  {
    //Set a random explorer square at the back
    std::swap(v.back(),v[ std::rand() % static_cast<int>(v.size())]);
    //Check possible adjacent squares
    const int x = v.back().first;
    const int y = v.back().second;
    std::vector<std::pair<int,int> > next;
    if (x > 0 + 2 && maze[y][x-2] == 2) next.push_back(std::make_pair(x-2,y));
    if (y > 0 + 2 && maze[y-2][x] == 2) next.push_back(std::make_pair(x,y-2));
    if (x < sz - 2 && maze[y][x+2] == 2) next.push_back(std::make_pair(x+2,y));
    if (y < sz - 2 && maze[y+2][x] == 2) next.push_back(std::make_pair(x,y+2));
    //Dead end?
    if (next.empty())
    {
      v.pop_back();
      continue;
    }
    //Select a random next adjacent square
    const int nextIndex = (std::rand() >> 4) % static_cast<int>(next.size());
    const int nextX = next[nextIndex].first;
    const int nextY = next[nextIndex].second;
    //Clear next square
    maze[nextY][nextX] = 0;
    //Clear path towards next square
    maze[(y + nextY)/2][(x + nextX)/2] = 0;
    //Add next square to stack
    v.push_back(std::make_pair(nextX,nextY));
  }
  return maze;
}

 

 

 

 

 

Licence

 

Maze Creator, creates a maze and displays it on screen
Copyright (C) 2007 Richel Bilderbeek

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/>.

 

 

 

 

 

Go back to Richel Bilderbeek's tools page.

Go back to Richel Bilderbeek's homepage.

 

Valid XHTML 1.0 Strict