|
|
|
/*---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
mod3.cpp implements a patience card game
|
|
|
|
|
|
|
|
Copyright (C) 1997 Rodolfo Borges
|
|
|
|
|
|
|
|
* Permission to use, copy, modify, and distribute this software and its
|
|
|
|
* documentation for any purpose and without fee is hereby granted,
|
|
|
|
* provided that the above copyright notice appear in all copies and that
|
|
|
|
* both that copyright notice and this permission notice appear in
|
|
|
|
* supporting documentation.
|
|
|
|
*
|
|
|
|
* This file is provided AS IS with no warranties of any kind. The author
|
|
|
|
* shall have no liability with respect to the infringement of copyrights,
|
|
|
|
* trade secrets or any patents by this file or any part thereof. In no
|
|
|
|
* event will the author be liable for any lost revenue or profits or
|
|
|
|
* other special, indirect and consequential damages.
|
|
|
|
|
|
|
|
---------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
#include "mod3.h"
|
|
|
|
#include "cardmaps.h"
|
|
|
|
#include <klocale.h>
|
|
|
|
#include "deck.h"
|
|
|
|
#include <kdebug.h>
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------//
|
|
|
|
|
|
|
|
Mod3::Mod3( TDEMainWindow* parent, const char* _name)
|
|
|
|
: Dealer( parent, _name )
|
|
|
|
{
|
|
|
|
const int dist_x = cardMap::CARDX() * 11 / 10 + 1;
|
|
|
|
const int dist_y = cardMap::CARDY() * 11 / 10 + 1;
|
|
|
|
const int margin = cardMap::CARDY() / 3;
|
|
|
|
|
|
|
|
// This patience uses 2 deck of cards.
|
|
|
|
deck = Deck::new_deck( this, 2);
|
|
|
|
deck->move(8 + dist_x * 8 + 20, 8 + dist_y * 3 + margin);
|
|
|
|
|
|
|
|
connect(deck, TQT_SIGNAL(clicked(Card*)), TQT_SLOT(deckClicked(Card*)));
|
|
|
|
|
|
|
|
aces = new Pile(50, this);
|
|
|
|
aces->move(16 + dist_x * 8, 8 + dist_y / 2);
|
|
|
|
aces->setTarget(true);
|
|
|
|
aces->setCheckIndex(2);
|
|
|
|
aces->setAddFlags(Pile::addSpread | Pile::several);
|
|
|
|
|
|
|
|
for ( int r = 0; r < 4; r++ ) {
|
|
|
|
for ( int c = 0; c < 8; c++ ) {
|
|
|
|
stack[r][c] = new Pile ( r * 10 + c + 1, this );
|
|
|
|
stack[r][c]->move( 8 + dist_x * c,
|
|
|
|
8 + dist_y * r + margin * ( r == 3 ));
|
|
|
|
|
|
|
|
// The first 3 rows are the playing field, the fourth is the store.
|
|
|
|
if ( r < 3 ) {
|
|
|
|
stack[r][c]->setCheckIndex( 0 );
|
|
|
|
stack[r][c]->setTarget(true);
|
|
|
|
} else {
|
|
|
|
stack[r][c]->setAddFlags( Pile::addSpread );
|
|
|
|
stack[r][c]->setCheckIndex( 1 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setTakeTargetForHints(true);
|
|
|
|
setActions(Dealer::Hint | Dealer::Demo );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------//
|
|
|
|
|
|
|
|
|
|
|
|
bool Mod3::checkAdd( int checkIndex, const Pile *c1, const CardList& cl) const
|
|
|
|
{
|
|
|
|
// kdDebug(11111) << "checkAdd " << checkIndex << " " << c1->top()->name() << " " << c1->index() << " " << c1->index() / 10 << endl;
|
|
|
|
if (checkIndex == 0) {
|
|
|
|
Card *c2 = cl.first();
|
|
|
|
|
|
|
|
if (c1->isEmpty())
|
|
|
|
return (c2->rank() == ( ( c1->index() / 10 ) + 2 ) );
|
|
|
|
|
|
|
|
kdDebug(11111) << "not empty\n";
|
|
|
|
|
|
|
|
if (c1->top()->suit() != c2->suit())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
kdDebug(11111) << "same suit\n";
|
|
|
|
if (c2->rank() != (c1->top()->rank()+3))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
kdDebug(11111) << "+3 " << c1->cardsLeft() << " " << c1->top()->rank() << " " << c1->index()+1 << endl;
|
|
|
|
if (c1->cardsLeft() == 1)
|
|
|
|
return (c1->top()->rank() == ((c1->index() / 10) + 2));
|
|
|
|
|
|
|
|
kdDebug(11111) << "+1\n";
|
|
|
|
|
|
|
|
return true;
|
|
|
|
} else if (checkIndex == 1) {
|
|
|
|
return c1->isEmpty();
|
|
|
|
} else if (checkIndex == 2) {
|
|
|
|
return cl.first()->rank() == Card::Ace;
|
|
|
|
} else return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Mod3::checkPrefering( int checkIndex, const Pile *c1, const CardList& c2) const
|
|
|
|
{
|
|
|
|
return (checkIndex == 0 && c1->isEmpty()
|
|
|
|
&& c2.first()->rank() == (c1->index()+1));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------//
|
|
|
|
|
|
|
|
|
|
|
|
void Mod3::restart()
|
|
|
|
{
|
|
|
|
deck->collectAndShuffle();
|
|
|
|
deal();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------//
|
|
|
|
|
|
|
|
|
|
|
|
void Mod3::dealRow(int row)
|
|
|
|
{
|
|
|
|
if (deck->isEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (int c = 0; c < 8; c++) {
|
|
|
|
Card *card;
|
|
|
|
|
|
|
|
card = deck->nextCard();
|
|
|
|
stack[row][c]->add (card, false, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Mod3::deckClicked(Card*)
|
|
|
|
{
|
|
|
|
kdDebug(11111) << "deck clicked " << deck->cardsLeft() << endl;
|
|
|
|
if (deck->isEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
unmarkAll();
|
|
|
|
dealRow(3);
|
|
|
|
takeState();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------//
|
|
|
|
|
|
|
|
|
|
|
|
void Mod3::deal()
|
|
|
|
{
|
|
|
|
unmarkAll();
|
|
|
|
CardList list = deck->cards();
|
|
|
|
/* for (CardList::Iterator it = list.begin(); it != list.end(); ++it)
|
|
|
|
if ((*it)->rank() == Card::Ace) {
|
|
|
|
aces->add(*it);
|
|
|
|
(*it)->hide();
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
kdDebug(11111) << "init " << aces->cardsLeft() << " " << deck->cardsLeft() << endl;
|
|
|
|
|
|
|
|
for (int r = 0; r < 4; r++)
|
|
|
|
dealRow(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
Card *Mod3::demoNewCards()
|
|
|
|
{
|
|
|
|
if (deck->isEmpty())
|
|
|
|
return 0;
|
|
|
|
deckClicked(0);
|
|
|
|
return stack[3][0]->top();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Mod3::startAutoDrop() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Mod3::isGameLost() const
|
|
|
|
{
|
|
|
|
int n,row,col;
|
|
|
|
kdDebug(11111) << "isGameLost ?"<< endl;
|
|
|
|
|
|
|
|
bool nextTest=false;
|
|
|
|
|
|
|
|
// If there is an empty stack or an ace below, the game is not lost.
|
|
|
|
for (col=0; col < 8; col++){
|
|
|
|
if (stack[3][col]->isEmpty()
|
|
|
|
|| stack[3][col]->at(0)->rank() == Card::Ace)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ok, so no empty stack below.
|
|
|
|
// If there is neither an empty stack on the board (an ace counts
|
|
|
|
// as this) nor a card placed in the correct row, all is lost.
|
|
|
|
// Otherwise we have to do more tests.
|
|
|
|
for (n = 0; n < 24; n++) {
|
|
|
|
row = n / 8;
|
|
|
|
col = n % 8;
|
|
|
|
|
|
|
|
// If there is a stack on the board that is either empty or
|
|
|
|
// contains an ace, the game is not finished.
|
|
|
|
if (stack[row][col]->isEmpty()
|
|
|
|
|| stack[row][col]->at(0)->rank() == Card::Ace) {
|
|
|
|
nextTest = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there is a card that is correctly placed, the game is
|
|
|
|
// not lost.
|
|
|
|
if (stack[row][col]->at(0)->rank() == Card::Two + row) {
|
|
|
|
nextTest = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!nextTest)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// If there are more cards in the deck, the game is not lost.
|
|
|
|
if (!deck->isEmpty())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
int n2, row2, col2, col3;
|
|
|
|
Card *ctop;
|
|
|
|
Card *card;
|
|
|
|
|
|
|
|
// For all stacks on the board, check if:
|
|
|
|
//
|
|
|
|
for (n = 0; n < 24; n++){
|
|
|
|
row = n / 8;
|
|
|
|
col = n % 8;
|
|
|
|
|
|
|
|
// Empty stack: Can we move a card there?
|
|
|
|
if (stack[row][col]->isEmpty()) {
|
|
|
|
// Can we move a card from below?
|
|
|
|
for (col3=0; col3 < 8; col3++) {
|
|
|
|
if (stack[3][col3]->top()->rank() == (Card::Two+row))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Can we move a card from another row?
|
|
|
|
for (n2 = 0; n2 < 16; n2++) {
|
|
|
|
row2 = (row + 1 + (n2 / 8)) % 3;
|
|
|
|
col2 = n2 % 8;
|
|
|
|
|
|
|
|
if (stack[row2][col2]->isEmpty())
|
|
|
|
continue;
|
|
|
|
if (stack[row2][col2]->top()->rank() == (Card::Two + row))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Non-empty stack.
|
|
|
|
ctop = stack[row][col]->top();
|
|
|
|
kdDebug(11111) << "considering ["<<row<<"]["<<col<<"] " << ctop->name() << flush;
|
|
|
|
|
|
|
|
// Card not in its final position? Then we can't build on it.
|
|
|
|
if (stack[row][col]->at(0)->rank() != Card::Two + row)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Can we move a card from below here?
|
|
|
|
for (col3 = 0; col3 < 8; col3++) {
|
|
|
|
card = stack[3][col3]->top();
|
|
|
|
if (card->suit() == ctop->suit()
|
|
|
|
&& card->rank() == ctop->rank() + 3)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
kdDebug(11111) <<" Can't stack from bottom row" << flush;
|
|
|
|
|
|
|
|
// Can we move a card from another stack here?
|
|
|
|
for (int n_2 = 1; n_2 < 24; n_2++) {
|
|
|
|
n2 = (n + n_2) % 24;
|
|
|
|
row2 = n2 / 8;
|
|
|
|
col2 = n2 % 8;
|
|
|
|
|
|
|
|
if (stack[row2][col2]->isEmpty())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
card = stack[row2][col2]->top();
|
|
|
|
|
|
|
|
// Only consider cards that are not on top of other cards.
|
|
|
|
if (stack[row2][col2]->indexOf(card) != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (card->suit() == ctop->suit()
|
|
|
|
&& card->rank() == ctop->rank() + 3)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static class LocalDealerInfo5 : public DealerInfo
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
LocalDealerInfo5() : DealerInfo(I18N_NOOP("M&od3"), 5) {}
|
|
|
|
virtual Dealer *createGame(TDEMainWindow *parent) { return new Mod3(parent); }
|
|
|
|
} ldi5;
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------//
|
|
|
|
|
|
|
|
#include"mod3.moc"
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------//
|
|
|
|
|