summaryrefslogtreecommitdiffstats
path: root/kpat/idiot.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kpat/idiot.cpp')
-rw-r--r--kpat/idiot.cpp234
1 files changed, 234 insertions, 0 deletions
diff --git a/kpat/idiot.cpp b/kpat/idiot.cpp
new file mode 100644
index 00000000..3ac49ef5
--- /dev/null
+++ b/kpat/idiot.cpp
@@ -0,0 +1,234 @@
+/*
+ idiot.cpp implements a patience card game
+
+ Copyright (C) 1995 Paul Olav Tvete
+
+ 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.
+
+ 4 positions, remove lowest card(s) of suit
+*/
+
+
+#include "idiot.h"
+#include <klocale.h>
+#include "deck.h"
+#include "cardmaps.h"
+
+
+Idiot::Idiot( KMainWindow* parent, const char* _name)
+ : Dealer( parent, _name )
+{
+ // Create the deck to the left.
+ m_deck = Deck::new_deck( this );
+ m_deck->move(10, 10);
+
+ const int distx = cardMap::CARDX() + cardMap::CARDX() / 10 + 1;
+
+ // Create 4 piles where the cards will be placed during the game.
+ for( int i = 0; i < 4; i++ ) {
+ m_play[i] = new Pile( i + 1, this);
+
+ m_play[i]->setAddFlags( Pile::addSpread );
+ m_play[i]->setRemoveFlags( Pile::disallow );
+ m_play[i]->move(10 + cardMap::CARDX() * 18 / 10 + distx * i, 10);
+ }
+
+ // Create the discard pile to the right
+ m_away = new Pile( 5, this );
+ m_away->setTarget(true);
+ m_away->setRemoveFlags(Pile::disallow);
+ m_away->move(10 + cardMap::CARDX() * 5 / 2 + distx * 4, 10);
+
+ setActions(Dealer::Hint | Dealer::Demo);
+}
+
+
+void Idiot::restart()
+{
+ m_deck->collectAndShuffle();
+ deal();
+}
+
+
+inline bool higher( const Card* c1, const Card* c2)
+{
+ // Sanity check.
+ if (!c1 || !c2 || c1 == c2)
+ return false;
+
+ // Must be same suit.
+ if (c1->suit() != c2->suit())
+ return false;
+
+ // Aces form a special case.
+ if (c2->rank() == Card::Ace)
+ return true;
+ if (c1->rank() == Card::Ace)
+ return false;
+
+ return (c1->rank() < c2->rank());
+}
+
+
+bool Idiot::canMoveAway(Card *c)
+{
+ return ( higher( c, m_play[ 0 ]->top() ) ||
+ higher( c, m_play[ 1 ]->top() ) ||
+ higher( c, m_play[ 2 ]->top() ) ||
+ higher( c, m_play[ 3 ]->top() ) );
+}
+
+
+bool Idiot::cardClicked(Card *c)
+{
+ // If the deck is clicked, deal 4 more cards.
+ if (c->source() == m_deck) {
+ deal();
+ return true;
+ }
+
+ // Only the top card of a pile can be clicked.
+ if (c != c->source()->top())
+ return false;
+
+ bool didMove = true;
+ if ( canMoveAway(c) )
+ // Add to 'm_away', face up, no spread
+ m_away->add(c, false, false);
+ else if ( m_play[ 0 ]->isEmpty() )
+ // Add to pile 1, face up, spread.
+ m_play[0]->add(c, false, true);
+ else if ( m_play[ 1 ]->isEmpty() )
+ // Add to pile 2, face up, spread.
+ m_play[1]->add(c, false, true);
+ else if ( m_play[ 2 ]->isEmpty() )
+ // Add to pile 3, face up, spread.
+ m_play[2]->add( c, false, true);
+ else if ( m_play[ 3 ]->isEmpty() )
+ // Add to pile 4, face up, spread.
+ m_play[3]->add(c, false, true);
+ else
+ didMove = false;
+
+ return true; // may be a lie, but noone cares
+}
+
+
+// The game is won when:
+// 1. all cards are dealt.
+// 2. all piles contain exactly one ace.
+// 3. the rest of the cards are thrown away (follows automatically from 1, 2.
+//
+bool Idiot::isGameWon() const
+{
+ // Criterium 1.
+ if (!m_deck->isEmpty())
+ return false;
+
+ // Criterium 2.
+ for (int i = 0; i < 4; i++) {
+ if (m_play[i]->cardsLeft() != 1 || m_play[i]->top()->rank() != Card::Ace)
+ return false;
+ }
+
+ return true;
+}
+
+
+// This patience doesn't support double click.
+//
+
+bool Idiot::cardDblClicked(Card *)
+{
+ return false; // nothing - nada
+}
+
+
+// Deal 4 cards face up - one on each pile.
+//
+
+void Idiot::deal()
+{
+ if ( m_deck->isEmpty() )
+ return;
+
+ // Move the four top cards of the deck to the piles, faceup, spread out.
+ for ( int i = 0; i < 4; i++ )
+ m_play[ i ]->add( m_deck->nextCard(), false, true );
+}
+
+
+void Idiot::getHints()
+{
+ bool cardMoved = false;
+ for ( int i = 0; i < 4; i++ )
+ if ( canMoveAway( m_play[i]->top() ) ) {
+ cardMoved = true;
+ newHint(new MoveHint(m_play[i]->top(), m_away));
+ }
+
+ if (cardMoved)
+ return;
+
+ // now let's try to be a bit clever with the empty piles
+ for( int i = 0; i < 4; i++ ) {
+ if (m_play[i]->isEmpty()) {
+ // Find a card to move there
+ int biggestPile = -1;
+ int sizeBiggestPile = -1;
+ for( int j = 0; j < 4; j++ ) {
+ if ( i != j && m_play[j]->cardsLeft()>1 ) {
+
+ // Ace on top of the pile? -> move it
+ if ( m_play[j]->top()->rank() == Card::Ace ) {
+ biggestPile = j;
+ break;
+ }
+
+ // Otherwise choose the biggest pile
+ if ( m_play[j]->cardsLeft() > sizeBiggestPile ) {
+ sizeBiggestPile = m_play[j]->cardsLeft();
+ biggestPile = j;
+ }
+ }
+ }
+
+ if ( biggestPile != -1 ) {
+ newHint(new MoveHint(m_play[biggestPile]->top(), m_play[i]));
+ return;
+ }
+ }
+ }
+}
+
+
+Card *Idiot::demoNewCards()
+{
+ if ( m_deck->isEmpty() )
+ return 0;
+
+ deal();
+
+ return m_play[0]->top();
+}
+
+
+static class LocalDealerInfo2 : public DealerInfo
+{
+public:
+ LocalDealerInfo2() : DealerInfo(I18N_NOOP("&Aces Up"), 2) {}
+ virtual Dealer *createGame(KMainWindow *parent) { return new Idiot(parent); }
+} ldi4;
+
+
+#include "idiot.moc"