RetroShare/plugins/qsolocards_plugin/YukonBoard.cpp
defnax 082d5732b0 added game plugin solocards
git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@2346 b45a01b8-16f6-495d-af2f-9b41ad6348cc
2010-02-17 01:14:52 +00:00

489 lines
13 KiB
C++

/*
QSoloCards is a collection of Solitaire card games written using Qt
Copyright (C) 2009 Steve Moore
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 "YukonBoard.h"
#include "CardPixmaps.h"
#include <QtGui/QResizeEvent>
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
YukonBoard::YukonBoard()
:GameBoard(NULL,QString(tr("Yukon Solitaire")).trimmed(),QString("Yukon")),
m_pDeck(NULL),
m_homeVector(),
m_stackVector(),
m_cheat(false)
{
this->setHelpFile(":/help/YukonHelp.html");
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
YukonBoard::~YukonBoard()
{
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
bool YukonBoard::getHint(CardStack * & pSrc,
unsigned int & srcStackIndex,
CardStack * & pDst)
{
bool rc=false;
unsigned int i;
// ok we will look for a move based on trying to move the first faceup
// card in a stack. The basic strategy for this game is to get all cards turned
// over.
if (!rc)
{
for (i=0;i<this->m_stackVector.size() && !rc;i++)
{
unsigned int j;
for (j=0;j<this->m_stackVector.size() && !rc;j++)
{
// can't move a stack to itself
if (i!=j)
{
PlayingCardVector addCardVector(this->m_stackVector[j]->getCardVector());
unsigned int k=0;
while (!rc && addCardVector.size()>0)
{
// ok find the first card that is faceup.
if (!addCardVector[0].isFaceUp())
{
addCardVector.erase(addCardVector.begin());
}
// no need to do this if a king is already face up and the first card in the stack.
else if (!(PlayingCard::King==addCardVector[0].getIndex() && 0==k &&
this->m_stackVector[i]->isEmpty()) &&
this->m_stackVector[i]->canAddCards(addCardVector))
{
pDst=this->m_stackVector[i];
srcStackIndex=k;
pSrc=this->m_stackVector[j];
rc=true;
}
else
{
break;
}
k++;
}
}
}
}
}
// ok let's see if we have an empty stack
// and a face up king.
if (!rc)
{
CardStack * pEmptyStack=NULL;
CardStack * pFaceUpKingStack=NULL;
unsigned int faceUpKingIndex=0;
for (i=0;i<this->m_stackVector.size() && !rc;i++)
{
if (NULL==pEmptyStack && this->m_stackVector[i]->isEmpty())
{
pEmptyStack=this->m_stackVector[i];
}
if (NULL==pFaceUpKingStack)
{
const PlayingCardVector cardVector=this->m_stackVector[i]->getCardVector();
unsigned int j;
for(j=0;j<cardVector.size();j++)
{
// look for a king that is not already face up and first in it's stack.
if (PlayingCard::King==cardVector[j].getIndex() &&
cardVector[j].isFaceUp() && 0!=j)
{
pFaceUpKingStack=m_stackVector[i];
faceUpKingIndex=j;
}
}
}
if (NULL!=pEmptyStack && NULL!=pFaceUpKingStack)
{
pSrc=pFaceUpKingStack;
srcStackIndex=faceUpKingIndex;
pDst=pEmptyStack;
rc=true;
}
}
}
// try to move cards to home stacks.
if (!rc)
{
for (i=0;i<this->m_homeVector.size() && !rc;i++)
{
unsigned int j;
for (j=0;j<this->m_stackVector.size() && !rc;j++)
{
const PlayingCardVector cardVector=this->m_stackVector[j]->getCardVector();
if (cardVector.size()>0)
{
PlayingCardVector addCardVector;
addCardVector.push_back(cardVector[cardVector.size()-1]);
if (this->m_homeVector[i]->canAddCards(addCardVector))
{
pDst=this->m_homeVector[i];
srcStackIndex=cardVector.size()-1;
pSrc=this->m_stackVector[j];
rc=true;
}
}
}
}
}
// look at each stack and try to find the first available card that from another
// stack that can be moved to the stack.
if (!rc)
{
for (i=0;i<this->m_stackVector.size() && !rc;i++)
{
unsigned int j;
for (j=0;j<this->m_stackVector.size() && !rc;j++)
{
// can't move a stack to itself
if (i!=j)
{
PlayingCardVector addCardVector(this->m_stackVector[j]->getCardVector());
unsigned int k=0;
while (!rc && addCardVector.size()>0)
{
// ok find the first card that is faceup.
if (!addCardVector[0].isFaceUp())
{
addCardVector.erase(addCardVector.begin());
}
else if (this->m_stackVector[i]->canAddCards(addCardVector))
{
pDst=this->m_stackVector[i];
srcStackIndex=k;
pSrc=this->m_stackVector[j];
rc=true;
}
else
{
addCardVector.erase(addCardVector.begin());
}
k++;
}
}
}
}
}
return rc;
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
void YukonBoard::newGame()
{
// call the base class
GameBoard::newGame();
CardDeck deck;
unsigned int i;
while(!deck.isEmpty())
{
this->m_pDeck->addCard(deck.next());
}
DealItemVector dealItemVector;
// Create the dealItemVector to direct the DealAnimation object on
// how to deal the cards.
for (i=0;i<this->m_stackVector.size();i++)
{
dealItemVector.push_back(DealItem(this->m_stackVector[i],m_pDeck));
unsigned int j;
for (j=0;j<i+1;j++)
{
// add the items to tell how to deal the cards to the stack
// we want to flip the last card in each stack.
if (i==j)
{
dealItemVector[i].addCard(true);
}
else
{
dealItemVector[i].addCard(false);
}
}
// for yukon all but the first stack get 4 more face up cards
if (i>0)
{
for (j=0;j<4;j++)
{
dealItemVector[i].addCard(true);
}
}
}
// ok now start the deal. We don't need a move record for this item.
m_dealAni.dealCards(dealItemVector,false);
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
void YukonBoard::addGameMenuItems(QMenu & menu)
{
Q_UNUSED(menu);
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
void YukonBoard::loadSettings(const QSettings & settings)
{
Q_UNUSED(settings);
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
void YukonBoard::saveSettings(QSettings & settings)
{
Q_UNUSED(settings);
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
void YukonBoard::setCheat(bool cheat)
{
this->m_cheat=cheat;
for(unsigned int i=0;i<this->m_stackVector.size();i++)
{
m_stackVector[i]->setCheat(cheat);
}
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
void YukonBoard::slotStackCardsClicked(CardStack * pCardStack,
const PlayingCardVector & cardVector,
const CardMoveRecord & startMoveRecord)
{
unsigned int i=0;
CardStack * pFoundStack=NULL;
if (NULL==pCardStack)
{
return;
}
// first see if the card can be added to the sent home stack
if (cardVector.size()==1)
{
for(i=0;i<this->m_homeVector.size();i++)
{
if (pCardStack!=this->m_homeVector[i] &&
this->m_homeVector[i]->canAddCards(cardVector))
{
pFoundStack=this->m_homeVector[i];
break;
}
}
}
// if we did not find a match look at the stacks.
if (NULL==pFoundStack)
{
for(i=0;i<this->m_stackVector.size();i++)
{
if (pCardStack!=this->m_stackVector[i] &&
this->m_stackVector[i]->canAddCards(cardVector))
{
pFoundStack=this->m_stackVector[i];
break;
}
}
}
if (pFoundStack)
{
CardMoveRecord moveRecord(startMoveRecord);
pFoundStack->addCards(cardVector,moveRecord,true);
// perform the move of the cards and animate it if animations
// are enabled
m_sToSAniMove.moveCards(moveRecord);
}
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
void YukonBoard::calcScore()
{
int score=0;
for(unsigned int i=0;i<this->m_homeVector.size();i++)
{
score+=this->m_homeVector[i]->score();
}
emit scoreChanged(score,"");
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
void YukonBoard::resizeEvent (QResizeEvent * event)
{
int i;
this->setCardResizeAlg(8,ResizeByWidth);
GameBoard::resizeEvent(event);
QSize cardSize(CardPixmaps::getInst().getCardSize());
// ok let's see if it fits when we size things by width
// basically we just need to check if we have room to draw
// 4 cards vertically. If we don't have enough room we
// will try to draw by height.
if (cardSize.height()*4+GameBoard::LayoutSpacing*5>event->size().height())
{
this->setCardResizeAlg(4,ResizeByHeight);
GameBoard::resizeEvent(event);
cardSize=CardPixmaps::getInst().getCardSize();
}
QPointF currPos(GameBoard::LayoutSpacing,GameBoard::LayoutSpacing);
for (i=0;i<static_cast<int>(m_homeVector.size());i++)
{
m_homeVector[i]->setPos(currPos);
currPos.ry()+=GameBoard::LayoutSpacing+cardSize.height();
}
currPos.setX(event->size().width()-GameBoard::LayoutSpacing-cardSize.width());
currPos.setY(GameBoard::LayoutSpacing);
for (i=m_stackVector.size()-1;i>=0;i--)
{
m_stackVector[i]->setPos(currPos);
currPos.rx()-=cardSize.width()+GameBoard::LayoutSpacing;
}
currPos.setX(GameBoard::LayoutSpacing*4+cardSize.width()*4);
currPos.setY(event->size().height()-GameBoard::LayoutSpacing-cardSize.height());
m_pDeck->setPos(currPos);
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
void YukonBoard::createStacks()
{
unsigned int i;
// first create the home widgets where the cards need to be eventually stacked to
// win the game.
for(i=0;i<4;i++)
{
this->m_homeVector.push_back(new FreeCellHome);
this->m_scene.addItem(m_homeVector[i]);
}
// now create the 7 rows for the stacks.
for (i=0;i<7;i++)
{
this->m_stackVector.push_back(new KlondikeStack);
this->m_scene.addItem(m_stackVector[i]);
this->connect(this->m_stackVector[i],SIGNAL(cardsMovedByDragDrop(CardMoveRecord)),
this,SLOT(slotCardsMoved(CardMoveRecord)));
this->connect(this->m_stackVector[i],SIGNAL(movableCardsClicked(CardStack*,PlayingCardVector,CardMoveRecord)),
this,SLOT(slotStackCardsClicked(CardStack*,PlayingCardVector,CardMoveRecord)));
}
this->m_pDeck=new FreeCellDeck;
this->m_scene.addItem(this->m_pDeck);
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
bool YukonBoard::isGameWon()const
{
bool rc=true;
for (unsigned int i=0;i<this->m_homeVector.size();i++)
{
if (!this->m_homeVector[i]->isStackComplete())
{
rc=false;
break;
}
}
return rc;
}
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
bool YukonBoard::isGameWonNotComplete()const
{
bool rc=true;
for (unsigned int i=0;i<this->m_stackVector.size();i++)
{
if (!(this->m_stackVector[i]->cardsAscendingTopToBottom() &&
this->m_stackVector[i]->allCardsFaceUp()))
{
rc=false;
break;
}
}
return rc;
}