//---------------------------------------------------------------------------
/*
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();
}
//---------------------------------------------------------------------------
|