Go back to Richel Bilderbeek's homepage.

Go back to Richel Bilderbeek's C++ page.

 

 

 

 

 

(C++) PylosWidget

 

PylosWidget is a Qt class to display a Pylos.

 

 

 

 

 

Technical facts

 

Application type(s)

Operating system(s) or programming environment(s)

IDE(s):

Project type:

C++ standard:

Compiler(s):

Libraries used:

 

 

 

 

 

pyloswidget.cpp

 

//---------------------------------------------------------------------------
/*
PylosWidget, widget to display Pylos class
Copyright (C) 2010 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/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/ToolTestPylos.htm
//---------------------------------------------------------------------------
#include <cassert>
#include <cstdlib>
#include <iostream>
//---------------------------------------------------------------------------
#include <boost/foreach.hpp>
#include <boost/numeric/conversion/cast.hpp>
//---------------------------------------------------------------------------
#include <QMouseEvent>
#include <QPainter>
//---------------------------------------------------------------------------
#include "pyloswidget.h"
//---------------------------------------------------------------------------
template <class T> const std::vector<T> CreateVector(const T& t)
{
  std::vector<T> v;
  v.push_back(t);
  return v;
}
//---------------------------------------------------------------------------
template <class T> const std::vector<T> CreateVector(
  const T& t, const T& u)
{
  std::vector<T> v;
  v.push_back(t);
  v.push_back(u);
  return v;
}
//---------------------------------------------------------------------------
PylosWidget::PylosWidget() :
    QWidget(0),
    m_sprites(this->width(),this->height(),GetRedBlueColors()),
    m_pylos(new PylosBasic),
    m_select(0,0,0)
{
  //Allows this widget to respond to mouse moving over it
  this->setMouseTracking(true);

  SetSelector(PylosCoordinat(0,0,0));

  SaveAllSprites();
  this->setMinimumWidth(64);
  this->setMinimumHeight(64);
}
//---------------------------------------------------------------------------
///mouseMoveEvent sets the selector coordinats
void PylosWidget::mouseMoveEvent(QMouseEvent * e)
{
  assert(e->type() == QMouseEvent::MouseMove);
  const int mouse_x = e->x();
  const int mouse_y = e->y();
  if (m_pylos->MustRemove())
    MouseMoveRemoval(mouse_x,mouse_y);
  else
    MouseMoveSelect(mouse_x,mouse_y);
}
//---------------------------------------------------------------------------
void PylosWidget::mousePressEvent(QMouseEvent* e)
{
  //std::clog << "void PylosWidget::mousePressEvent(QMouseEvent* e)\n";
  switch (e->button())
  {
    case Qt::LeftButton : MouseLeftClick(); break;
    case Qt::RightButton: MouseRightClick(); break;
    default: break;
  }
}
//---------------------------------------------------------------------------
void PylosWidget::paintEvent(QPaintEvent *)
{
  assert(m_pylos);
  QPainter painter(this);

  painter.drawPixmap(0,0,m_sprites.Get(Sprites::board_bottom));
  //Draw the hole
  for (int y=0; y!=4; ++y)
    for (int x=0; x!=4; ++x)
      painter.drawPixmap(
        x * m_sprites.GetMarbleWidth(),
        y * m_sprites.GetMarbleHeight(),
        m_sprites.Get(Sprites::board_hole));

  for (int layer=0; layer!=4; ++layer)
  {
    const int layer_size = m_pylos->GetLayerSize(layer);
    for (int y=0; y!=layer_size; ++y)
    {
      for (int x=0; x!=layer_size; ++x)
      {
        assert(PylosCoordinat::IsValid(layer,x,y));
        const PylosCoordinat c(layer,x,y);

        //Draw selector, after sprite is drawn
        if (c == m_select) DrawSelect(painter);

        const int state = m_pylos->Get(c);
        int sprite = 0;
        switch (state)
        {
          case Pylos::empty  : continue;
          case Pylos::player1: sprite = Sprites::player1; break;
          case Pylos::player2: sprite = Sprites::player2; break;
          assert(!"Should not get here");
        }
        painter.drawPixmap(
          x * m_sprites.GetMarbleWidth()  + (layer * (m_sprites.GetMarbleWidth()  / 2)),
          y * m_sprites.GetMarbleHeight() + (layer * (m_sprites.GetMarbleHeight() / 2)),
          m_sprites.Get(state));

        //Draw remove
        BOOST_FOREACH(PylosCoordinat d,m_other_selectors)
        {
          if (d == c) DrawRemove(painter,c);
        }

        //Draw selector, after sprite is drawn
        if (c == m_select) DrawSelect(painter);
      }
    }
  }
}
//---------------------------------------------------------------------------
void PylosWidget::resizeEvent(QResizeEvent *)
{
  m_sprites.SetBoardSize(this->width(),this->height());
  repaint();
}
//---------------------------------------------------------------------------
///DeselectRemove remove coordinat c from m_other_selectors,
///because the player deselected the marble at
///that coordinat
void PylosWidget::DeselectRemove(const PylosCoordinat& c)
{
  assert(!m_other_selectors.empty());
  const int sz = boost::numeric_cast<int>(m_other_selectors.size());
  assert(sz == 1 || sz == 2);
  if (sz == 1)
  {
    assert(m_other_selectors[0] == c);
    m_other_selectors.pop_back();
    return;
  }
  assert(sz == 2);
  if (m_other_selectors[0] == c)
  {
    assert(m_other_selectors[1] != c);
    std::swap(m_other_selectors[0],m_other_selectors[1]);
    assert(m_other_selectors.back() == c);
    m_other_selectors.pop_back();
  }
  else
  {
    assert(m_other_selectors[1] == c);
    m_other_selectors.pop_back();
  }
}
//---------------------------------------------------------------------------
///DrawRemove draws a marble toggled for removal
void PylosWidget::DrawRemove(QPainter& painter, const PylosCoordinat& c)
{
  const int sprite =
    ( m_pylos->Get(c) == Pylos::player1
     ? static_cast<int>(Sprites::player1_remove)
     : static_cast<int>(Sprites::player2_remove) );
  painter.drawPixmap(
      c.GetX() * m_sprites.GetMarbleWidth()  + (c.GetLayer() * (m_sprites.GetMarbleWidth()  / 2)),
      c.GetY() * m_sprites.GetMarbleHeight() + (c.GetLayer() * (m_sprites.GetMarbleHeight() / 2)),
      m_sprites.Get(sprite));
}
//---------------------------------------------------------------------------
///DrawSelect draws the selector
void PylosWidget::DrawSelect(QPainter& painter)
{
  if (m_pylos->GetWinner() != Pylos::no_winner) return;
  const int sprite
    = (m_pylos->GetCurrentTurn() == Pylos::player1
    ? static_cast<int>(Sprites::player1_select)
    : static_cast<int>(Sprites::player2_select));
  painter.drawPixmap(
    m_select.GetX() * m_sprites.GetMarbleWidth()  + (m_select.GetLayer() * (m_sprites.GetMarbleWidth()  / 2)),
    m_select.GetY() * m_sprites.GetMarbleHeight() + (m_select.GetLayer() * (m_sprites.GetMarbleHeight() / 2)),
    m_sprites.Get(sprite));
}
//---------------------------------------------------------------------------
///GetCurrentTurn returns the player whose turn it is
int PylosWidget::GetCurrentTurn() const
{
  const int current_turn = m_pylos->GetCurrentTurn();
  switch (current_turn)
  {
    case Pylos::player1: return player1;
    case Pylos::player2: return player2;
    default: assert(!"Should not get here");
  }
}
//---------------------------------------------------------------------------
const std::vector<std::string> PylosWidget::GetVersionHistory()
{
  std::vector<std::string> v;
  v.push_back("YYYY-MM-DD: version X.Y: [description]");
  v.push_back("2010-09-22: version 1.2: initial release version");
  v.push_back("2010-10-06: version 1.3: disallow clicking when there is a winner");
  return v;
}
//---------------------------------------------------------------------------
int PylosWidget::GetWinner() const
{
  const int winner = m_pylos->GetWinner();
  switch (winner)
  {
    case Pylos::no_winner: return no_winner;
    case Pylos::player1: return player1;
    case Pylos::player2: return player2;
    default: assert(!"Should not get here");
  }
}
//---------------------------------------------------------------------------
///IsOtherSelector returns if the specified coordinat
///is selected for removal
bool PylosWidget::IsOtherSelector(const PylosCoordinat& c) const
{
  BOOST_FOREACH(const PylosCoordinat& d,m_other_selectors)
  {
    if (d == c) return true;
  }
  return false;
}
//---------------------------------------------------------------------------
///MouseLeftClick handles mouse left-clicking.
void PylosWidget::MouseLeftClick()
{
  if (m_pylos->MustRemove())
    MouseLeftClickRemove();
  else
    MouseLeftClickSelect();
}
//---------------------------------------------------------------------------
///MouseLeftClickRemove handles mouse left-clicking
///during removal state.
void PylosWidget::MouseLeftClickRemove()
{
  //Player tries to select a third marble
  if (m_other_selectors.size() == 2)
  {
    assert(m_select == m_other_selectors[0] || m_select == m_other_selectors[1]);
    DeselectRemove(m_select);
    repaint();
    emit Toggle();
    return;
  }

  //Toggle marbles selected for removal
  BOOST_FOREACH(const PylosCoordinat& d,m_other_selectors)
  {
    if (m_select == d)
    {
      //Remove c from m_other_selectors,
      //invalidates d
      DeselectRemove(m_select);
      repaint();
      emit Toggle();
      return;
    }
  }

  //Player toggles his first marble for removal
  if (m_other_selectors.empty())
  {
    if (m_pylos->CanRemove(CreateVector(m_select))) m_other_selectors.push_back(m_select);
    repaint();
    emit Toggle();
    return;
  }

  assert(m_other_selectors.size() == 1);
  //Player clicks a marble and has selected none or one other
  //If the player can remove the selected marble
  //and if he has not selected two marbles
  //for removal already
  if (m_pylos->CanRemove(CreateVector(m_select,m_other_selectors[0])))
  {
    m_other_selectors.push_back(m_select);
    repaint();
    emit Toggle();
  }
}
//---------------------------------------------------------------------------
///MouseLeftClickSelect handles mouse left-clicking
///during select state.
void PylosWidget::MouseLeftClickSelect()
{
  if (m_pylos->GetWinner() != Pylos::no_winner) return;

  //Select marble for movement
  if (m_other_selectors.empty() && m_pylos->CanMove(m_select))
  {
    m_other_selectors.push_back(m_select);
    repaint();
    emit Toggle();
    return;
  }
  //Toggle marble selected for movement
  if (!m_other_selectors.empty() && m_select == m_other_selectors[0])
  {
    m_other_selectors.pop_back();
    repaint();
    emit Toggle();
    return;
  }
  //Add marbles
  if (m_other_selectors.empty() && m_pylos->CanPlace(m_select))
  {
    m_pylos->Place(m_select);
    m_other_selectors = std::vector<PylosCoordinat>();
    repaint();
    emit PlayerChanged();
    if (m_pylos->GetWinner() != Pylos::no_winner)
      emit HasWinner();
    return;
  }

    //User might want to move a marble
  if (!m_pylos->MustRemove())
  {
    if (!m_other_selectors.empty()
      && m_pylos->CanMove(m_other_selectors[0], m_select) )
    {
      m_pylos->Move(m_other_selectors[0],m_select);
      emit PlayerChanged();
      m_other_selectors = std::vector<PylosCoordinat>();
      repaint();
    }
    return;
  }

}
//---------------------------------------------------------------------------
///MouseMoveRemoval handles mouse movement
///when player must remove one or two marbles
void PylosWidget::MouseMoveRemoval(
  const int mouse_x,
  const int mouse_y)
{
  //Selector must be set to removable marbles
  //Check heighest Pylos level
  for (int layer=3; layer!=-1; --layer)
  {
    const int x = (mouse_x - ((m_sprites.GetMarbleWidth()  / 2) *layer)) / m_sprites.GetMarbleWidth();
    const int y = (mouse_y - ((m_sprites.GetMarbleHeight() / 2) *layer)) / m_sprites.GetMarbleHeight();

    if (!PylosCoordinat::IsValid(layer,x,y)) continue;
    PylosCoordinat c(layer,x,y);
    if (
       //player has selected two marbles for removal,
       //only select those marbles
      (m_other_selectors.size() == 2 && IsOtherSelector(c))
       //player has selected one marble for removal,
       //select the marble (possibly below it) to be
       //removed as well
    || (m_other_selectors.size() == 1 && m_pylos->CanRemove(CreateVector(m_other_selectors[0],c)))
       //player has selected nothing for removal
    || (m_other_selectors.empty() && m_pylos->CanRemove(CreateVector(c)))
      )
    {
      SetSelector(c);
      repaint();
      return;
    }
  }
}
//---------------------------------------------------------------------------
///MouseMoveSelect handles mouse movement
///when player must select either a location to
///place a new marble or to select a marble to move
void PylosWidget::MouseMoveSelect(
  const int mouse_x, const int mouse_y)
{
  //Selector must show to either
  //-$ movable marbles
  //-$ spots to place a new marble
  //Check lowest Pylos level
  for (int layer=0; layer!=4; ++layer)
  {
    const int x = (mouse_x - ((m_sprites.GetMarbleWidth()  / 2) *layer)) / m_sprites.GetMarbleWidth();
    const int y = (mouse_y - ((m_sprites.GetMarbleHeight() / 2) *layer)) / m_sprites.GetMarbleHeight();
    if (!PylosCoordinat::IsValid(layer,x,y)) continue;
    PylosCoordinat c(layer,x,y);
    if (
         ( m_other_selectors.empty() && (m_pylos->CanPlace(c)  || m_pylos->CanMove(c)                      ) )
      || (!m_other_selectors.empty() && (IsOtherSelector(c)    || m_pylos->CanMove(m_other_selectors[0],c) ) ) )
    {
      SetSelector(c);
      repaint();
      return;
    }
  }
}
//---------------------------------------------------------------------------
///MouseRightClick handles mouse right-clicking.
void PylosWidget::MouseRightClick()
{
  if (!m_pylos->MustRemove()) return;
  //Right mouse button is only used to remove the
  //marbles selected for removal
  //There must be marbles selected
  if (m_other_selectors.empty()) return;
  //Two marbles are selected
  if (m_pylos->CanRemove(m_other_selectors))
  {
    m_pylos->Remove(m_other_selectors);
    emit PlayerChanged();
    m_other_selectors = std::vector<PylosCoordinat>();
    repaint();
  }
}
//---------------------------------------------------------------------------
void PylosWidget::SaveAllSprites() const
{
  m_sprites.Get(Sprites::player1).save("sprite_player1.png");
  m_sprites.Get(Sprites::player2).save("sprite_player2.png");
  m_sprites.Get(Sprites::player1_select).save("sprite_player1_select.png");
  m_sprites.Get(Sprites::player2_select).save("sprite_player2_select.png");
  m_sprites.Get(Sprites::player1_remove).save("sprite_player1_remove.png");
  m_sprites.Get(Sprites::player2_remove).save("sprite_player2_remove.png");
  m_sprites.Get(Sprites::board_bottom).save("sprite_board_bottom.png");
  m_sprites.Get(Sprites::board_hole).save("sprite_board_hole.png");
}
//---------------------------------------------------------------------------
///SetColorSchemeBlackWhite sets the color scheme to black and white.
void PylosWidget::SetColorSchemeBlackWhite()
{
  m_sprites.SetColorScheme(::GetBlackWhiteColors());
  repaint();
}
//---------------------------------------------------------------------------
///SetColorSchemeRedBlue sets the color scheme to red and blue.
void PylosWidget::SetColorSchemeRedBlue()
{
  m_sprites.SetColorScheme(::GetRedBlueColors());
  repaint();
}
//---------------------------------------------------------------------------
///SetSelector sets the selector coordinat to c
void PylosWidget::SetSelector(const PylosCoordinat& c)
{
  m_select = c;
  emit SelectorChanged();
}
//---------------------------------------------------------------------------
///StartAdvancedGame cleans the board to start a game
///with advanced rules
void PylosWidget::StartAdvancedGame()
{
  m_pylos.reset(new PylosAdvanced);
  m_select = PylosCoordinat(0,0,0);
  m_other_selectors = std::vector<PylosCoordinat>();
  repaint();
}
//---------------------------------------------------------------------------
///StartAdvancedGame cleans the board to start a game
///with advanced rules
void PylosWidget::StartBasicGame()
{
  m_pylos.reset(new PylosBasic);
  m_select = PylosCoordinat(0,0,0);
  m_other_selectors = std::vector<PylosCoordinat>();
  repaint();
}
//---------------------------------------------------------------------------

 

 

 

 

 

pyloswidget.h

 

//---------------------------------------------------------------------------
/*
PylosWidget, widget to display Pylos class
Copyright (C) 2010 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/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/ToolTestPylos.htm
//---------------------------------------------------------------------------
#ifndef PYLOSWIDGET_H
#define PYLOSWIDGET_H
//---------------------------------------------------------------------------
#include <vector>
//---------------------------------------------------------------------------
#include <boost/tuple/tuple.hpp>
#include <boost/shared_ptr.hpp>
//---------------------------------------------------------------------------
#include <QWidget>
//---------------------------------------------------------------------------
#include "pyloscoordinat.h"
#include "sprites.h"
//---------------------------------------------------------------------------
struct QPaintEvent;
struct Pylos;
//---------------------------------------------------------------------------
///PylosWidget manages a Pylos and facilitates its user interface
class PylosWidget : public QWidget
{
  Q_OBJECT
public:
  PylosWidget();
  ///mouseMoveEvent is public, because
  ///TestPylos must be able to make virtual mouse movements.
  void mouseMoveEvent(QMouseEvent * e);
  ///mousePressEvent is public, because
  ///TestPylos must be able to deliver virtual clicks.
  void mousePressEvent(QMouseEvent*);

  //void DrawPylos();
  enum { no_winner = 0 };
  enum { player1   = 1 };
  enum { player2   = 2 };

  ///GetCurrentTurn returns the player whose turn it is
  int GetCurrentTurn() const;

  ///GetPylos returns a read-only pylos
  const Pylos * GetPylos() { return m_pylos.get(); }

  ///GetOtherSelectors returns the other selectors' current coodinats
  const std::vector<PylosCoordinat>& GetOtherSelectors() const
  {
    return m_other_selectors;
  }

  ///GetSelector returns the selector's current coodinat
  const PylosCoordinat& GetSelector() const { return m_select; }

  static const std::string GetVersion() { return "1.3"; }
  static const std::vector<std::string> GetVersionHistory();

  ///GetWinner returns either the current winner or no_winner
  int GetWinner() const;

  ///SetColorSchemeBlackWhite sets the color scheme to black and white.
  void SetColorSchemeBlackWhite();

  ///SetColorSchemeRedBlue sets the color scheme to red and blue.
  void SetColorSchemeRedBlue();

  ///StartAdvancedGame cleans the board to start a game
  ///with advanced rules
  void StartAdvancedGame();

  ///StartBasicGame cleans the board to start a game
  ///with basic rules
  void StartBasicGame();


signals:
  ///HasWinner is emitted when a winner is found
  void HasWinner();
  ///SelectorChanged is emitted when the selector is moved
  void SelectorChanged();
  ///PlayerChanged is emitted when a player has done a successfull move
  void PlayerChanged();
  ///Toggle is emitted when a marble is (de)selected for something
  void Toggle();

protected:
  void paintEvent(QPaintEvent *);
  void resizeEvent(QResizeEvent *);

private:
  Sprites m_sprites;
  boost::shared_ptr<Pylos> m_pylos;
  PylosCoordinat m_select;

  ///m_other_selectors embodies the coordinats for
  ///-$ selecting a marble to move to a higher layer
  ///-$ select one or two marbles for removal
  std::vector<PylosCoordinat> m_other_selectors;

  ///DeselectRemove remove coordinat c from m_other_selectors,
  ///because the player deselected the marble at
  ///that coordinat
  void DeselectRemove(const PylosCoordinat& c);

  ///DrawRemove draws a marble toggled for removal
  void DrawRemove(QPainter& painter, const PylosCoordinat& c);

  ///DrawSelect draws the selector
  void DrawSelect(QPainter& painter);

  ///IsOtherSelector returns if the specified coordinat
  ///is selected for removal
  bool IsOtherSelector(const PylosCoordinat& c) const;

  ///MouseLeftClick handles mouse left-clicking.
  void MouseLeftClick();

  ///MouseLeftClickRemove handles mouse left-clicking
  ///during removal state.
  void MouseLeftClickRemove();

  ///MouseLeftClickSelect handles mouse left-clicking
  ///during select state.
  void MouseLeftClickSelect();

  ///MouseRightClick handles mouse right-clicking.
  void MouseRightClick();

  ///MouseMoveRemoval handles mouse movement
  ///when player must remove one or two marbles
  void MouseMoveRemoval(const int x, const int y);

  ///MouseMoveSelect handles mouse movement
  ///when player must select either a location to
  ///place a new marble or to select a marble to move
  void MouseMoveSelect(const int x, const int y);

  ///SaveAllSprites saves all sprites used in this game.
  void SaveAllSprites() const;

  ///SetSelector sets the selector coordinat to c
  void SetSelector(const PylosCoordinat& c);

};
//---------------------------------------------------------------------------
#endif // PYLOSWIDGET_H

 

 

 

 

 

Go back to Richel Bilderbeek's C++ page.

Go back to Richel Bilderbeek's homepage.

 

Valid XHTML 1.0 Strict