RetroShare/plugins/qsolocards_plugin/FreeCellBoard.cpp

694 lines
18 KiB
C++
Raw Normal View History

/*
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 "FreeCellBoard.h"
#include "CardPixmaps.h"
#include "CardDeck.h"
#include "CardAnimationLock.h"
#include <iostream>
#include <QtGui/QMessageBox>
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
FreeCellBoard::FreeCellBoard()
:GameBoard(NULL,QString(tr("Freecell")).trimmed(),QString("Freecell")),
m_pDeck(NULL),
m_freeVector(),
m_stackVector(),
m_homeVector(),
m_cheat(false)
{
this->setHelpFile(":/help/FreeCellHelp.html");
}
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
FreeCellBoard::~FreeCellBoard()
{
}
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
void FreeCellBoard::undoMove()
{
GameBoard::undoMove();
this->setNumStackMoveCards();
}
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
void FreeCellBoard::redoMove()
{
GameBoard::redoMove();
this->setNumStackMoveCards();
}
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
bool FreeCellBoard::getHint(CardStack * & pSrc,
unsigned int & srcIndex,
CardStack * & pDst)
{
bool rc=false;
unsigned int i;
unsigned int j;
// first see if we have any cards from the free cells to move
// home
for (i=0;i<this->m_freeVector.size() && !rc;i++)
{
if (!this->m_freeVector[i]->isEmpty())
{
const PlayingCardVector & cardVector=this->m_freeVector[i]->getCardVector();
PlayingCardVector moveCards;
srcIndex=cardVector.size()-1;
moveCards.push_back(cardVector[srcIndex]);
for (j=0;j<this->m_homeVector.size();j++)
{
if (this->m_homeVector[j]->canAddCards(moveCards))
{
pSrc=this->m_freeVector[i];
pDst=this->m_homeVector[j];
rc=true;
break;
}
}
}
}
// now see if there are any cards in the stacks to move home
if (!rc)
{
for (i=0;i<this->m_stackVector.size() && !rc;i++)
{
if (!this->m_stackVector[i]->isEmpty())
{
const PlayingCardVector & cardVector=this->m_stackVector[i]->getCardVector();
PlayingCardVector moveCards;
srcIndex=cardVector.size()-1;
moveCards.push_back(cardVector[srcIndex]);
for (j=0;j<this->m_homeVector.size();j++)
{
if (this->m_homeVector[j]->canAddCards(moveCards))
{
pSrc=this->m_stackVector[i];
pDst=this->m_homeVector[j];
rc=true;
break;
}
}
}
}
}
// now see if we can move cards from the free cells to the stacks
if (!rc)
{
for (i=0;i<this->m_freeVector.size() && !rc;i++)
{
if (!this->m_freeVector[i]->isEmpty())
{
PlayingCardVector moveCards;
if (this->m_freeVector[i]->getMovableCards(moveCards,srcIndex))
{
for (j=0;j<this->m_stackVector.size();j++)
{
if (this->m_stackVector[j]->canAddCards(moveCards))
{
pSrc=this->m_freeVector[i];
pDst=this->m_stackVector[j];
rc=true;
break;
}
}
}
}
}
}
// now look for stack to stack moves
if (!rc)
{
for (i=0;i<this->m_stackVector.size() && !rc;i++)
{
if (!this->m_stackVector[i]->isEmpty())
{
PlayingCardVector moveCards;
if (this->m_stackVector[i]->getMovableCards(moveCards,srcIndex))
{
// look through the stacks if we couldn't move the card home
for (j=0;j<this->m_stackVector.size();j++)
{
// make sure not the same stackVector and that we are not moving
// the last card in a stack to an empty stack. Moving the last
// card in a stack to an empty stack doesn't do anything. And lastly
// if the cards can be added we have a match.
if (i!=j &&
!(0==srcIndex && this->m_stackVector[j]->isEmpty()) &&
this->m_stackVector[j]->canAddCards(moveCards))
{
pSrc=this->m_stackVector[i];
pDst=this->m_stackVector[j];
rc=true;
break;
}
}
}
}
}
}
// now look to move something to a free cell if all else has failed
// for now just going to be a basic move an available card from the first
// stack with cards in it to the free cell.
if (!rc)
{
// first find an open free cell. If we don't have one no need to continue.
pDst=NULL;
for (i=0;i<this->m_freeVector.size();i++)
{
if (this->m_freeVector[i]->isEmpty())
{
pDst=this->m_freeVector[i];
break;
}
}
if (NULL!=pDst)
{
for (i=0;i<this->m_stackVector.size();i++)
{
if (!this->m_stackVector[i]->isEmpty())
{
PlayingCardVector moveCards;
if (this->m_stackVector[i]->getMovableCards(moveCards,srcIndex) &&
pDst->canAddCards(moveCards))
{
pSrc=this->m_stackVector[i];
rc=true;
break;
}
}
}
}
}
return rc;
}
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
void FreeCellBoard::newGame()
{
// call the base class to clean up
GameBoard::newGame();
CardDeck deck;
unsigned int i;
// add all the cards to the deck
while(!deck.isEmpty())
{
this->m_pDeck->addCard(deck.next());
}
// setup the deal of cards
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++)
{
unsigned int j;
unsigned int cardsInStack=((i<4)?7:6);
dealItemVector.push_back(DealItem(this->m_stackVector[i],m_pDeck));
for (j=0;j<cardsInStack;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 FreeCellBoard::addGameMenuItems(QMenu & menu)
{
Q_UNUSED(menu);
}
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
void FreeCellBoard::loadSettings(const QSettings & settings)
{
Q_UNUSED(settings);
}
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
void FreeCellBoard::saveSettings(QSettings & settings)
{
Q_UNUSED(settings);
}
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
void FreeCellBoard::setCheat(bool cheat)
{
unsigned int i;
for (i=0;i<m_stackVector.size();i++)
{
m_stackVector[i]->setCheat(cheat);
}
}
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
void FreeCellBoard::slotCardsMoved(const CardMoveRecord & moveRecord)
{
GameBoard::slotCardsMoved(moveRecord);
this->setNumStackMoveCards();
}
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
void FreeCellBoard::slotFreeCardsClicked(CardStack * pCardStack,
const PlayingCardVector & cardVector,
const CardMoveRecord & startMoveRecord)
{
Q_UNUSED(pCardStack);
CardStack * pEmptyStack=NULL;
CardStack * pMoveStack=NULL;
unsigned int i;
// ok look for where we would move the cards to. We will look first at the home stacks.
// After the home stacks the next priority will be a non-empty stack and then lastly
// an empty stack.
for (i=0;i<this->m_homeVector.size();i++)
{
if (this->m_homeVector[i]->canAddCards(cardVector))
{
pMoveStack=this->m_homeVector[i];
break;
}
}
// if we did not find a match continue to look
if (NULL==pMoveStack)
{
for (i=0;i<this->m_stackVector.size();i++)
{
if (this->m_stackVector[i]->canAddCards(cardVector))
{
if (this->m_stackVector[i]->isEmpty())
{
pEmptyStack=this->m_stackVector[i];
}
else
{
pMoveStack=this->m_stackVector[i];
break;
}
}
}
}
if (NULL!=pMoveStack || NULL!=pEmptyStack)
{
if (NULL==pMoveStack)
{
pMoveStack=pEmptyStack;
}
CardMoveRecord moveRecord(startMoveRecord);
pMoveStack->addCards(cardVector,moveRecord,true);
// perform the move of the cards and animate it if animations
// are enabled
m_sToSAniMove.moveCards(moveRecord);
}
}
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
void FreeCellBoard::slotStackCardsClicked(CardStack * pCardStack,
const PlayingCardVector & cardVector,
const CardMoveRecord & startMoveRecord)
{
CardStack * pEmptyStack=NULL;
CardStack * pMoveStack=NULL;
unsigned int i;
// ok look for where we would move the cards to. We will look first at the home stacks.
// After the home stacks the next priority will be a non-empty stack then an empty stack.
// And lastly a free cell.
for (i=0;i<this->m_homeVector.size();i++)
{
if (this->m_homeVector[i]->canAddCards(cardVector))
{
pMoveStack=this->m_homeVector[i];
break;
}
}
// if we did not find a match continue to look
if (NULL==pMoveStack)
{
for (i=0;i<this->m_stackVector.size();i++)
{
if (this->m_stackVector[i]!=pCardStack)
{
if (this->m_stackVector[i]->canAddCards(cardVector))
{
if (this->m_stackVector[i]->isEmpty())
{
pEmptyStack=this->m_stackVector[i];
}
else
{
pMoveStack=this->m_stackVector[i];
break;
}
}
}
}
}
// now look in the free cells
if (NULL==pMoveStack)
{
for (i=0;i<this->m_freeVector.size();i++)
{
if (this->m_freeVector[i]->canAddCards(cardVector))
{
pMoveStack=this->m_freeVector[i];
break;
}
}
}
if (NULL!=pMoveStack || NULL!=pEmptyStack)
{
if (NULL==pMoveStack)
{
pMoveStack=pEmptyStack;
}
CardMoveRecord moveRecord(startMoveRecord);
pMoveStack->addCards(cardVector,moveRecord,true);
// perform the move of the cards and animate it if animations
// are enabled
m_sToSAniMove.moveCards(moveRecord);
}
}
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
void FreeCellBoard::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 FreeCellBoard::resizeEvent (QResizeEvent * event)
{
unsigned int i;
GameBoard::resizeEvent(event);
QSize cardSize(CardPixmaps::getInst().getCardSize());
QPointF currPos(GameBoard::LayoutSpacing,GameBoard::LayoutSpacing);
for (i=0;i<m_freeVector.size();i++)
{
m_freeVector[i]->setPos(currPos);
currPos.rx()+=cardSize.width()+GameBoard::LayoutSpacing;
}
currPos.rx()+=(cardSize.width() + GameBoard::LayoutSpacing)/2;
m_pDeck->setPos(currPos);
currPos.setX(GameBoard::LayoutSpacing*7 + cardSize.width()*6);
for (i=0;i<m_homeVector.size();i++)
{
m_homeVector[i]->setPos(currPos);
currPos.rx()+=cardSize.width()+GameBoard::LayoutSpacing;
}
currPos.setY(GameBoard::LayoutSpacing*2+cardSize.height());
currPos.setX(GameBoard::LayoutSpacing*2+cardSize.width());
for (i=0;i<m_stackVector.size();i++)
{
m_stackVector[i]->setPos(currPos);
currPos.rx()+=cardSize.width()+GameBoard::LayoutSpacing;
}
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
bool FreeCellBoard::runDemo(bool stopWhenNoMore)
{
bool rc=true;
if (!GameBoard::runDemo(false))
{
// if we didn't find a move. Try to just move a card to a freecell.
CardStack * pDst=NULL;
bool foundMove=false;
CardStack * pSrc=NULL;
unsigned int srcIndex=0;
PlayingCardVector moveCards;
unsigned int i;
for (i=0;i<this->m_freeVector.size();i++)
{
if (this->m_freeVector[i]->isEmpty())
{
pDst=this->m_freeVector[i];
break;
}
}
if (NULL!=pDst)
{
for (i=0;i<this->m_stackVector.size();i++)
{
if (!this->m_stackVector[i]->isEmpty())
{
if (this->m_stackVector[i]->getMovableCards(moveCards,srcIndex) &&
pDst->canAddCards(moveCards))
{
pSrc=this->m_stackVector[i];
foundMove=true;
break;
}
}
}
}
// if we found a move create the move record. And call the stack to stack
// animation to move it.
if (foundMove)
{
CardMoveRecord moveRecord;
pSrc->removeCardsStartingAt(srcIndex,moveCards,moveRecord,true);
pDst->addCards(moveCards,moveRecord,true);
m_sToSAniMove.moveCards(moveRecord,this->getDemoCardAniTime());
}
else
{
if (stopWhenNoMore)
{
stopDemo();
rc=false;
}
else
{
rc=false;
}
}
}
return rc;
}
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
void FreeCellBoard::createStacks()
{
this->setCardResizeAlg(10,ResizeByWidth);
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 free cells
for(i=0;i<4;i++)
{
this->m_freeVector.push_back(new FreeCellFree);
this->m_scene.addItem(m_freeVector[i]);
this->connect(this->m_freeVector[i],SIGNAL(cardsMovedByDragDrop(CardMoveRecord)),
this,SLOT(slotCardsMoved(CardMoveRecord)));
this->connect(this->m_freeVector[i],SIGNAL(movableCardsClicked(CardStack*,PlayingCardVector,CardMoveRecord)),
this,SLOT(slotFreeCardsClicked(CardStack*,PlayingCardVector,CardMoveRecord)));
}
// now create the 8 rows for the stacks.
for (i=0;i<8;i++)
{
this->m_stackVector.push_back(new FreeCellStack);
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 FreeCellBoard::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 FreeCellBoard::isGameWonNotComplete()const
{
bool rc=true;
for (unsigned int i=0;i<this->m_stackVector.size();i++)
{
if (!this->m_stackVector[i]->cardsAscendingTopToBottom())
{
rc=false;
break;
}
}
return rc;
}
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
void FreeCellBoard::setNumStackMoveCards()
{
// as a convience allow dragging of cards if there would be enough
// freecells to move the cards
unsigned int numDragCards=1;
unsigned int i;
for (i=0;i<m_stackVector.size();i++)
{
if (m_stackVector[i]->isEmpty())
{
numDragCards++;
}
}
// in the case that we might be dragging the cards to a free
// space, we don't want the free space to count. It would not
// be a free space to dump a card. So, it can't be counted.
if (numDragCards>1)
{
numDragCards--;
}
for (i=0;i<m_freeVector.size();i++)
{
if (m_freeVector[i]->isEmpty())
{
numDragCards++;
}
}
for (i=0;i<m_stackVector.size();i++)
{
m_stackVector[i]->setMaxMoveCards(numDragCards);
}
}