summaryrefslogtreecommitdiff
path: root/tableau.c
diff options
context:
space:
mode:
Diffstat (limited to 'tableau.c')
-rw-r--r--tableau.c301
1 files changed, 301 insertions, 0 deletions
diff --git a/tableau.c b/tableau.c
new file mode 100644
index 0000000..9160a8c
--- /dev/null
+++ b/tableau.c
@@ -0,0 +1,301 @@
+/*
+ * Spider-Arachnid: A plugin for the Video Disk Recorder
+ *
+ * See the README file for copyright information and how to reach the author.
+ *
+ * $Id$
+ */
+
+#include "tableau.h"
+#include "deck.h"
+#include "heap.h"
+#include "history.h"
+
+
+/** --- class Tableau ------------------------------------------------------ **/
+
+/** Constructor */
+Tableau::Tableau(Deck& deck, int pileCount, int finalCount, int deals) :
+ dealCount(deals), deck(deck), piles(pileCount), finals(finalCount)
+{
+ cardsToOpen = deck.count() - (deals + 1) * pileCount;
+ for ( ; cardsToOpen < 0; cardsToOpen += pileCount)
+ --deals;
+
+ pack = new Pack(deck);
+ for (unsigned int p = 0; p < piles.size(); ++p)
+ piles[p] = new Pile(deck);
+ for (unsigned int f = 0; f < finals.size(); ++f)
+ finals[f] = new FinalHeap(deck);
+
+ history = new History();
+
+ // choice of piles for extra deal with remaining cards
+ Piles extra(cardsToOpen % pileCount);
+ if (!extra.empty())
+ {
+ int extraMax = extra.size() - 1;
+ int pilesMax = piles.size() - 1;
+ for (int e = 0; e <= extraMax / 2; ++e)
+ {
+ int p = (e * pilesMax) / extraMax;
+ extra[e] = piles[p];
+ extra[extraMax - e] = piles[pilesMax - p];
+ }
+ }
+
+ // deal cards to open
+ pack->initialDeal(piles, cardsToOpen / pileCount, extra);
+
+ // deal one open row
+ pack->deal(piles);
+
+ selected = 0;
+}
+
+/** Destructor */
+Tableau::~Tableau()
+{
+ delete pack;
+ for (unsigned int p = 0; p < piles.size(); ++p)
+ delete piles[p];
+ for (unsigned int f = 0; f < finals.size(); ++f)
+ delete finals[f];
+ delete history;
+}
+
+/** Current count of deals */
+int Tableau::deals()
+{
+ return dealCount - pack->count() / piles.size();
+}
+
+/** Current count of points */
+int Tableau::points()
+{
+ int openCard = 10;
+ int openPile = 15;
+ int matchingCard = 2;
+ int readyFinal = 50;
+ int bonusFinal = 2;
+ int bonusfreeFinals = 3;
+
+ int points = openCard * cardsToOpen;
+ for (unsigned int p = 0; p < piles.size(); ++p)
+ {
+ if (piles[p]->count() > piles[p]->open())
+ points -= openCard * (piles[p]->count() - piles[p]->open());
+ else
+ points += openPile;
+ points += matchingCard * piles[p]->getMatching();
+ }
+ int emptyFinals = 0;
+ int bonusFinals = 0;
+ for (unsigned int f = 0; f < finals.size(); ++f)
+ if (finals[f]->empty())
+ ++emptyFinals;
+ else if (finals[f]->getBonus())
+ ++bonusFinals;
+ points += readyFinal * (finals.size() - emptyFinals);
+ if (emptyFinals == 0 && bonusFinals > bonusfreeFinals)
+ points += bonusFinal * (bonusFinals - bonusfreeFinals);
+ return points;
+}
+
+/** Is no pile empty? */
+bool Tableau::noPileEmpty()
+{
+ for (unsigned int p = 0; p < piles.size(); p++)
+ if (piles[p]->empty())
+ return false;
+ return true;
+}
+
+/** Matches all cards in all piles? */
+bool Tableau::allCardsMatches()
+{
+ for (unsigned int p = 0; p < piles.size(); ++p)
+ if (piles[p]->count() > piles[p]->open() ||
+ piles[p]->count() > piles[p]->getMatching() *
+ deck.cardsInSuit / (deck.cardsInSuit - 1))
+ return false;
+ return true;
+}
+
+/** Is the game over? */
+bool Tableau::gameOver()
+{
+ for (unsigned int p = 0; p < piles.size(); p++)
+ if (!piles[p]->empty())
+ return false;
+ return true;
+}
+
+/** Select p-th pile by selecting up to max matching cards on its end */
+void Tableau::select(int p, int max)
+{
+ if (!piles[p]->empty())
+ {
+ unselect();
+ selected = piles[p];
+ selected->select(max);
+ changed = true;
+ }
+}
+
+/** Unselect the selected pile */
+void Tableau::unselect()
+{
+ if (selected)
+ {
+ selected->unselect();
+ selected = 0;
+ }
+}
+
+/** Move cards from selected pile to p-th pile */
+void Tableau::move(int p)
+{
+ selected->adaptSelectionTo(piles[p]);
+ int count = selected->selected();
+ if (count > 0)
+ {
+ bool turn = (count == selected->open() && count < selected->count());
+ history->add(new NormalMove(selected, piles[p], count, turn));
+ history->current()->execute();
+ }
+ unselect();
+ changed = true;
+}
+
+/** Search move from p-th pile to the next left pile, return destination */
+int Tableau::autoMoveLeft(int p)
+{
+ int i = -1;
+ if (!piles[p]->empty())
+ {
+ if (selected != piles[p])
+ select(p);
+ for (i = p - 1; i >= 0; --i)
+ if (piles[i]->empty() || selected->selectionMatchesTo(piles[i]))
+ break;
+ if (i >= 0)
+ move(i);
+ changed = true;
+ }
+ return i;
+}
+
+/** Search move from p-th pile to the next right pile, return destination */
+int Tableau::autoMoveRight(int p)
+{
+ int i = -1;
+ if (!piles[p]->empty())
+ {
+ if (selected != piles[p])
+ select(p);
+ for (i = p + 1; i < (int)piles.size(); ++i)
+ if (piles[i]->empty() || selected->selectionMatchesTo(piles[i]))
+ break;
+ if (i < (int)piles.size())
+ move(i);
+ else
+ i = -1;
+ changed = true;
+ }
+ return i;
+}
+
+/** Search best move from p-th pile, return destination */
+int Tableau::autoMove(int p)
+{
+ int i = -1;
+ if (!piles[p]->empty())
+ {
+ if (selected != piles[p])
+ select(p);
+ if (allCardsMatches() && selected->selected() == deck.cardsInSuit)
+ remove();
+ else
+ {
+ i = p;
+ while ((i = (i + 1) % piles.size()) != p)
+ if (selected->selectionMatchesTo(piles[i], true))
+ break;
+ if (i == p)
+ while ((i = (i + 1) % piles.size()) != p)
+ if (selected->selectionMatchesTo(piles[i], false))
+ break;
+ if (i == p)
+ while ((i = (i + 1) % piles.size()) != p)
+ if (piles[i]->empty())
+ break;
+ if (i != p)
+ move(i);
+ else
+ i = -1;
+ }
+ }
+ return i;
+}
+
+/** Deal one row */
+void Tableau::deal()
+{
+ if (!pack->empty() && noPileEmpty())
+ {
+ history->add(new DealMove(pack, piles));
+ history->current()->execute();
+
+ unselect();
+ changed = true;
+ }
+}
+
+/** Remove one suit of cards from selected pile to the final heaps */
+void Tableau::remove()
+{
+ int count = selected->selected();
+ if (count == deck.cardsInSuit)
+ {
+ unsigned int f;
+ for (f = 0; f < finals.size(); ++f)
+ if (finals[f]->empty())
+ break;
+ if (f < finals.size())
+ {
+ bool turn = (count == selected->open() && count < selected->count());
+ bool bonus = allCardsMatches();
+ history->add(new FinalMove(selected, finals[f], count, turn, bonus));
+ history->current()->execute();
+ }
+ unselect();
+ changed = true;
+ }
+}
+
+/** Go one move backward in the history */
+void Tableau::backward()
+{
+ if (history->movesExecuted())
+ {
+ history->current()->takeBack();
+ history->backward();
+
+ unselect();
+ changed = true;
+ }
+}
+
+/** Go one move forward in the history */
+void Tableau::forward()
+{
+ if (history->movesToExecute())
+ {
+ history->forward();
+ history->current()->execute();
+
+ unselect();
+ changed = true;
+ }
+}