[games/kpat] /: Added several Freecell game variants:
Albert Astals Cid
null at kde.org
Thu Mar 11 23:56:38 GMT 2021
Git commit 96474ebab01f45bef41cfa34b23ddb39179debfe by Albert Astals Cid, on behalf of Michael Lang.
Committed on 11/03/2021 at 23:56.
Pushed by aacid into branch 'master'.
Added several Freecell game variants:
Baker's Game
Eight Off
Forecell
Seahaven Towers
Other custom configurations
M +22 -0 doc/index.docbook
M +8 -2 src/dealerinfo.h
M +352 -85 src/freecell.cpp
M +34 -4 src/freecell.h
M +15 -0 src/kpat.kcfg
M +1 -1 src/patsolve/abstract_fc_solve_solver.h
M +44 -25 src/patsolve/freecellsolver.cpp
M +1 -1 src/patsolve/freecellsolver.h
M +1 -0 src/patsolve/patsolve.cpp
https://invent.kde.org/games/kpat/commit/96474ebab01f45bef41cfa34b23ddb39179debfe
diff --git a/doc/index.docbook b/doc/index.docbook
index 484926a..0dc0218 100644
--- a/doc/index.docbook
+++ b/doc/index.docbook
@@ -333,6 +333,28 @@ The maximum amount of cards you can move is calculated by:
(#{free cells} + 1) * 2<superscript>#{free piles}</superscript>
</para></sidebar>
+<variablelist>
+<varlistentry><term>Variations:</term>
+<listitem>
+<para>
+- Baker's Game is like Freecell, but the piles are built down by suit.
+</para>
+
+<para>
+- Eight Off is like Freecell, but the piles are built down by suit. You have 8 reserves and only kings can fill empty spaces. Four reserves are filled at game start.
+</para>
+
+<para>
+- Forecell is like Freecell, but the reserves are filled at game start, and only kings can fill empty spaces.
+</para>
+
+<para>
+- Seahaven Towers is like Freecell, but with 10 piles which are built down by suit, and only kings can fill empty spaces. Two reserves are filled at game start.
+</para>
+</listitem>
+</varlistentry>
+</variablelist>
+
<para>
To solve this game it is recommended to grab the cards out of the playing
sequences in the same order they have to be put into the foundation (first the
diff --git a/src/dealerinfo.h b/src/dealerinfo.h
index 0438553..56b1eb3 100644
--- a/src/dealerinfo.h
+++ b/src/dealerinfo.h
@@ -56,7 +56,7 @@ public:
KlondikeDrawOneId = 0,
GrandfatherId = 1,
AcesUpId = 2,
- FreecellId = 3,
+ FreecellGeneralId = 3,
Mod3Id = 5,
GypsyId = 7,
FortyAndEightId = 8,
@@ -75,7 +75,13 @@ public:
BakersDozenSpanishId= 21,
BakersDozenCastlesId= 22,
BakersDozenPortugueseId= 23,
- BakersDozenCustomId = 24
+ BakersDozenCustomId = 24,
+ FreecellId = 30,
+ FreecellBakersId = 31,
+ FreecellEightOffId = 32,
+ FreecellForeId = 33,
+ FreecellSeahavenId = 34,
+ FreecellCustomId = 39
};
DealerInfo( const QByteArray & untranslatedBaseName, int baseId );
diff --git a/src/freecell.cpp b/src/freecell.cpp
index 001acc2..ed9c374 100644
--- a/src/freecell.cpp
+++ b/src/freecell.cpp
@@ -46,32 +46,36 @@
#include "patsolve/freecellsolver.h"
// KF
#include <KLocalizedString>
-
+#include <kwidgetsaddons_version.h>
+#include <KSelectAction>
Freecell::Freecell( const DealerInfo * di )
: DealerScene( di )
{
+ configOptions();
+ getSavedOptions();
}
void Freecell::initialize()
{
- setDeckContents();
+ setDeckContents( m_decks + 1 );
- const qreal topRowDist = 1.08;
+ const bool isRightFoundation = m_reserves + 4 * (m_decks + 1) > (m_stacks + 6);
+ const qreal topRowDist = isRightFoundation ? 1.13 : 1.08;
const qreal bottomRowDist = 1.13;
- const qreal targetOffsetDist = ( 7 * bottomRowDist + 1 ) - ( 3 * topRowDist + 1 );
+ const qreal targetOffsetDist = ( (m_stacks + 5) * bottomRowDist + 1 ) - ( 3 * topRowDist + 1 ) * (m_decks + 1);
- for ( int i = 0; i < 4; ++i )
+ for ( int i = 0; i < m_reserves; ++i )
{
- freecell[i] = new PatPile ( this, 1 + 8 + i, QStringLiteral( "freecell%1" ).arg( i ) );
+ freecell[i] = new PatPile ( this, 1 + i, QStringLiteral( "freecell%1" ).arg( i ) );
freecell[i]->setPileRole(PatPile::Cell);
freecell[i]->setLayoutPos(topRowDist * i, 0);
freecell[i]->setKeyboardSelectHint( KCardPile::AutoFocusTop );
freecell[i]->setKeyboardDropHint( KCardPile::AutoFocusTop );
}
- for ( int i = 0; i < 8; ++i )
+ for ( int i = 0; i < (m_stacks + 6); ++i )
{
store[i] = new PatPile( this, 1 + i, QStringLiteral( "store%1" ).arg( i ) );
store[i]->setPileRole(PatPile::Tableau);
@@ -82,14 +86,31 @@ void Freecell::initialize()
store[i]->setKeyboardDropHint( KCardPile::AutoFocusTop );
}
- for ( int i = 0; i < 4; ++i )
+ if ( isRightFoundation )
{
- target[i] = new PatPile(this, 1 + 8 + 4 + i, QStringLiteral( "target%1" ).arg( i ));
- target[i]->setPileRole(PatPile::Foundation);
- target[i]->setLayoutPos(targetOffsetDist + topRowDist * i, 0);
- target[i]->setSpread(0, 0);
- target[i]->setKeyboardSelectHint( KCardPile::NeverFocus );
- target[i]->setKeyboardDropHint( KCardPile::ForceFocusTop );
+ const int columns = std::max(m_reserves, m_stacks + 6);
+ for ( int i = 0; i < 4 * (m_decks + 1); ++i )
+ {
+ const qreal offsetX = 0.25 + columns * bottomRowDist + i / 4 * bottomRowDist;
+ const qreal offsetY = bottomRowDist * i - i / 4 * bottomRowDist * 4;
+ target[i] = new PatPile( this, 1 + i, QStringLiteral("target%1").arg(i) );
+ target[i]->setPileRole(PatPile::Foundation);
+ target[i]->setLayoutPos(offsetX, offsetY);
+ target[i]->setKeyboardSelectHint( KCardPile::NeverFocus );
+ target[i]->setKeyboardDropHint( KCardPile::ForceFocusTop );
+ }
+ }
+ else
+ {
+ for ( int i = 0; i < 4 * (m_decks + 1); ++i )
+ {
+ target[i] = new PatPile(this, 1 + i, QStringLiteral( "target%1" ).arg( i ));
+ target[i]->setPileRole(PatPile::Foundation);
+ target[i]->setLayoutPos(targetOffsetDist + topRowDist * i, 0);
+ target[i]->setSpread(0, 0);
+ target[i]->setKeyboardSelectHint( KCardPile::NeverFocus );
+ target[i]->setKeyboardDropHint( KCardPile::ForceFocusTop );
+ }
}
setActions(DealerScene::Demo | DealerScene::Hint);
@@ -100,15 +121,80 @@ void Freecell::initialize()
}
+QList<QAction*> Freecell::configActions() const
+{
+ return QList<QAction*>() << options << m_emptyStackFillOption << m_sequenceBuiltByOption << m_reservesOption << m_stacksOption;
+}
+
+
+void Freecell::gameTypeChanged()
+{
+ stopDemo();
+
+ if ( allowedToStartNewGame() )
+ {
+ // remove existing piles
+ for ( int i = 0; i < m_reserves; ++i )
+ removePile(freecell[i]);
+
+ for ( int i = 0; i < (m_stacks + 6); ++i )
+ removePile(store[i]);
+
+ for ( int i = 0; i < 4 * (m_decks + 1); ++i )
+ removePile(target[i]);
+
+
+ if ( m_variation != options->currentItem() )
+ {
+ setOptions(options->currentItem());
+ }
+ else
+ {
+ // update option selections
+ if ( m_emptyStackFill != m_emptyStackFillOption->currentItem() )
+ m_emptyStackFill = m_emptyStackFillOption->currentItem();
+ else if ( m_sequenceBuiltBy != m_sequenceBuiltByOption->currentItem() )
+ m_sequenceBuiltBy = m_sequenceBuiltByOption->currentItem();
+ else if ( m_reserves != m_reservesOption->currentItem() )
+ m_reserves = m_reservesOption->currentItem();
+ else if ( m_stacks != m_stacksOption->currentItem() )
+ m_stacks = m_stacksOption->currentItem();
+ else if ( m_decks != m_decksOption->currentItem() )
+ m_decks = m_decksOption->currentItem();
+
+ matchVariant();
+ }
+
+ initialize();
+ relayoutScene();
+ startNew( gameNumber() );
+ setSavedOptions();
+ }
+ else
+ {
+ // If we're not allowed, reset the options
+ getSavedOptions();
+ }
+}
+
+
void Freecell::restart( const QList<KCard*> & cards )
{
QList<KCard*> cardList = cards;
+ // Prefill reserves for select game types
+ if ( m_variation == 4 )
+ for ( int i = 0; i < 2; ++i )
+ addCardForDeal( freecell[i], cardList.takeLast(), true, freecell[0]->pos() );
+ else if ( m_variation == 1 || m_variation == 2 )
+ for ( int i = 0; i < 4; ++i )
+ addCardForDeal( freecell[i], cardList.takeLast(), true, freecell[0]->pos() );
+
int column = 0;
while ( !cardList.isEmpty() )
{
addCardForDeal( store[column], cardList.takeLast(), true, store[0]->pos() );
- column = (column + 1) % 8;
+ column = (column + 1) % (m_stacks + 6);
}
startDealAnimation();
@@ -119,7 +205,7 @@ QString Freecell::solverFormat() const
{
QString output;
QString tmp;
- for (int i = 0; i < 4 ; i++) {
+ for (int i = 0; i < 4 * (m_decks + 1) ; i++) {
if (target[i]->isEmpty())
continue;
tmp += suitToString(target[i]->topCard()->suit()) + QLatin1Char('-') + rankToString(target[i]->topCard()->rank()) + QLatin1Char(' ');
@@ -128,7 +214,7 @@ QString Freecell::solverFormat() const
output += QStringLiteral("Foundations: %1\n").arg(tmp);
tmp.truncate(0);
- for (int i = 0; i < 4 ; i++) {
+ for (int i = 0; i < m_reserves ; i++) {
const auto fc = freecell[i];
tmp += (fc->isEmpty() ? QStringLiteral("-") : cardToRankSuitString(fc->topCard())) + QLatin1Char(' ');
}
@@ -138,11 +224,12 @@ QString Freecell::solverFormat() const
output += a.arg(tmp);
}
- for (int i = 0; i < 8 ; i++)
+ for (int i = 0; i < (m_stacks + 6) ; i++)
cardsListToLine(output, store[i]->cards());
return output;
}
+
void Freecell::cardsDroppedOnPile( const QList<KCard*> & cards, KCardPile * pile )
{
if ( cards.size() <= 1 )
@@ -152,12 +239,12 @@ void Freecell::cardsDroppedOnPile( const QList<KCard*> & cards, KCardPile * pile
}
QList<KCardPile*> freeCells;
- for ( int i = 0; i < 4; ++i )
+ for ( int i = 0; i < m_reserves; ++i )
if ( freecell[i]->isEmpty() )
freeCells << freecell[i];
QList<KCardPile*> freeStores;
- for ( int i = 0; i < 8; ++i )
+ for ( int i = 0; i < (m_stacks + 6); ++i )
if ( store[i]->isEmpty() && store[i] != pile )
freeStores << store[i];
@@ -177,7 +264,7 @@ bool Freecell::tryAutomaticMove(KCard *c)
if (allowedToRemove(c->pile(), c)
&& c == c->pile()->topCard())
{
- for (int i = 0; i < 4; i++)
+ for (int i = 0; i < m_reserves; i++)
{
if (allowedToAdd( freecell[i], {c} ))
{
@@ -189,25 +276,44 @@ bool Freecell::tryAutomaticMove(KCard *c)
return false;
}
+
bool Freecell::canPutStore( const KCardPile * pile, const QList<KCard*> & cards ) const
{
int freeCells = 0;
- for ( int i = 0; i < 4; ++i )
+ for ( int i = 0; i < m_reserves; ++i )
if ( freecell[i]->isEmpty() )
++freeCells;
int freeStores = 0;
- for ( int i = 0; i < 8; ++i )
- if ( store[i]->isEmpty() && store[i] != pile )
- ++freeStores;
+ if (m_emptyStackFill == 0)
+ {
+ for ( int i = 0; i < (m_stacks + 6); ++i )
+ if ( store[i]->isEmpty() && store[i] != pile)
+ ++freeStores;
+ }
- return cards.size() <= (freeCells + 1) << freeStores
- && ( pile->isEmpty()
- || ( pile->topCard()->rank() == cards.first()->rank() + 1
- && pile->topCard()->color() != cards.first()->color() ) );
+ if (cards.size() <= (freeCells + 1) << freeStores)
+ {
+ if (pile->isEmpty())
+ return m_emptyStackFill == 0 || (m_emptyStackFill == 1 && cards.first()->rank() == KCardDeck::King);
+ else
+ if (m_sequenceBuiltBy == 1)
+ return cards.first()->rank() == pile->topCard()->rank() - 1
+ && cards.first()->suit() == pile->topCard()->suit();
+ else if (m_sequenceBuiltBy == 0)
+ return cards.first()->rank() == pile->topCard()->rank() - 1
+ && pile->topCard()->color() != cards.first()->color();
+ else
+ return cards.first()->rank() == pile->topCard()->rank() - 1;
+ }
+ else
+ {
+ return false;
+ }
}
+
bool Freecell::checkAdd(const PatPile * pile, const QList<KCard*> & oldCards, const QList<KCard*> & newCards) const
{
switch (pile->pileRole())
@@ -223,12 +329,18 @@ bool Freecell::checkAdd(const PatPile * pile, const QList<KCard*> & oldCards, co
}
}
+
bool Freecell::checkRemove(const PatPile * pile, const QList<KCard*> & cards) const
{
switch (pile->pileRole())
{
case PatPile::Tableau:
- return isAlternateColorDescending(cards);
+ if (m_sequenceBuiltBy == 1)
+ return isSameSuitDescending(cards);
+ else if (m_sequenceBuiltBy == 0)
+ return isAlternateColorDescending(cards);
+ else
+ return isRankDescending(cards);
case PatPile::Cell:
return cards.first() == pile->topCard();
case PatPile::Foundation:
@@ -237,66 +349,20 @@ bool Freecell::checkRemove(const PatPile * pile, const QList<KCard*> & cards) co
}
}
-QList<MoveHint> Freecell::getHints()
-{
- QList<MoveHint> hintList = getSolverHints();
-
- if ( isDemoActive() )
- return hintList;
-
- const auto patPiles = this->patPiles();
- for (PatPile * store : patPiles) {
- if (store->isEmpty())
- continue;
-
- QList<KCard*> cards = store->cards();
- while (cards.count() && !cards.first()->isFaceUp())
- cards.erase(cards.begin());
-
- QList<KCard*>::Iterator iti = cards.begin();
- while (iti != cards.end())
- {
- if (allowedToRemove(store, (*iti)))
- {
- const auto patPiles = this->patPiles();
- for (PatPile * dest : patPiles) {
- int cardIndex = store->indexOf(*iti);
- if (cardIndex == 0 && dest->isEmpty() && !dest->isFoundation())
- continue;
-
- if (!checkAdd(dest, dest->cards(), cards))
- continue;
-
- if ( dest->isFoundation() ) // taken care by solver
- continue;
-
- QList<KCard*> cardsBelow = cards.mid(0, cardIndex);
- // if it could be here as well, then it's no use
- if ((cardsBelow.isEmpty() && !dest->isEmpty()) || !checkAdd(store, cardsBelow, cards))
- {
- hintList << MoveHint( *iti, dest, 0 );
- }
- else if (checkPrefering( dest, dest->cards(), cards )
- && !checkPrefering( store, cardsBelow, cards ))
- { // if checkPrefers says so, we add it nonetheless
- hintList << MoveHint( *iti, dest, 0 );
- }
- }
- }
- cards.erase(iti);
- iti = cards.begin();
- }
- }
- return hintList;
-}
-
static class FreecellDealerInfo : public DealerInfo
{
public:
FreecellDealerInfo()
- : DealerInfo(I18N_NOOP("Freecell"), FreecellId)
- {}
+ : DealerInfo(I18N_NOOP("Freecell"), FreecellGeneralId)
+ {
+ addSubtype( FreecellBakersId, I18N_NOOP( "Baker's Game" ) );
+ addSubtype( FreecellEightOffId, I18N_NOOP( "Eight Off" ) );
+ addSubtype( FreecellForeId, I18N_NOOP( "Forecell" ) );
+ addSubtype( FreecellId, I18N_NOOP( "Freecell" ) );
+ addSubtype( FreecellSeahavenId, I18N_NOOP( "Seahaven Towers" ) );
+ addSubtype( FreecellCustomId, I18N_NOOP( "Freecell (Custom)" ) );
+ }
DealerScene *createGame() const override
{
@@ -305,4 +371,205 @@ public:
} freecellDealerInfo;
+void Freecell::matchVariant()
+{
+ if ( m_emptyStackFill == 0 && m_sequenceBuiltBy == 1 && m_reserves == 4 && m_stacks == 2 )
+ m_variation = 0;
+ else if ( m_emptyStackFill == 1 && m_sequenceBuiltBy == 1 && m_reserves == 8 && m_stacks == 2 )
+ m_variation = 1;
+ else if ( m_emptyStackFill == 1 && m_sequenceBuiltBy == 0 && m_reserves == 4 && m_stacks == 2 )
+ m_variation = 2;
+ else if ( m_emptyStackFill == 0 && m_sequenceBuiltBy == 0 && m_reserves == 4 && m_stacks == 2 )
+ m_variation = 3;
+ else if ( m_emptyStackFill == 1 && m_sequenceBuiltBy == 1 && m_reserves == 4 && m_stacks == 4 )
+ m_variation = 4;
+ else
+ m_variation = 5;
+
+ options->setCurrentItem( m_variation );
+}
+
+
+void Freecell::configOptions()
+{
+ options = new KSelectAction(i18n("Popular Variant Presets"), this );
+ options->addAction( i18n("Baker's Game") );
+ options->addAction( i18n("Eight Off") );
+ options->addAction( i18n("Forecell") );
+ options->addAction( i18n("Freecell") );
+ options->addAction( i18n("Seahaven Towers") );
+ options->addAction( i18n("Custom") );
+
+ m_emptyStackFillOption = new KSelectAction(i18n("Empty Stack Fill"), this );
+ m_emptyStackFillOption->addAction( i18n("Any (Easy)") );
+ m_emptyStackFillOption->addAction( i18n("Kings only (Medium)") );
+ m_emptyStackFillOption->addAction( i18n("None (Hard)") );
+
+ m_sequenceBuiltByOption = new KSelectAction(i18n("Build Sequence"), this );
+ m_sequenceBuiltByOption->addAction( i18n("Alternating Color") );
+ m_sequenceBuiltByOption->addAction( i18n("Matching Suit") );
+ m_sequenceBuiltByOption->addAction( i18n("Rank") );
+
+ m_reservesOption = new KSelectAction(i18n("Free cells"), this );
+ m_reservesOption->addAction( i18n("0") );
+ m_reservesOption->addAction( i18n("1") );
+ m_reservesOption->addAction( i18n("2") );
+ m_reservesOption->addAction( i18n("3") );
+ m_reservesOption->addAction( i18n("4") );
+ m_reservesOption->addAction( i18n("5") );
+ m_reservesOption->addAction( i18n("6") );
+ m_reservesOption->addAction( i18n("7") );
+ m_reservesOption->addAction( i18n("8") );
+
+ m_stacksOption = new KSelectAction(i18n("Stacks"), this );
+ m_stacksOption->addAction( i18n("6") );
+ m_stacksOption->addAction( i18n("7") );
+ m_stacksOption->addAction( i18n("8") );
+ m_stacksOption->addAction( i18n("9") );
+ m_stacksOption->addAction( i18n("10") );
+ m_stacksOption->addAction( i18n("11") );
+ m_stacksOption->addAction( i18n("12") );
+
+ m_decksOption = new KSelectAction(i18n("Decks"), this );
+ m_decksOption->addAction( i18n("1") );
+ m_decksOption->addAction( i18n("2") );
+ m_decksOption->addAction( i18n("3") );
+
+#if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 78, 0)
+ connect(options, &KSelectAction::indexTriggered, this, &Freecell::gameTypeChanged);
+ connect(m_emptyStackFillOption, &KSelectAction::indexTriggered, this, &Freecell::gameTypeChanged);
+ connect(m_reservesOption, &KSelectAction::indexTriggered, this, &Freecell::gameTypeChanged);
+ connect(m_sequenceBuiltByOption, &KSelectAction::indexTriggered, this, &Freecell::gameTypeChanged);
+ connect(m_stacksOption, &KSelectAction::indexTriggered, this, &Freecell::gameTypeChanged);
+ connect(m_decksOption, &KSelectAction::indexTriggered, this, &Freecell::gameTypeChanged);
+#else
+ connect(options, static_cast<void (KSelectAction::*)(int)>(&KSelectAction::triggered), this, &Freecell::gameTypeChanged);
+ connect(m_emptyStackFillOption, static_cast<void (KSelectAction::*)(int)>(&KSelectAction::triggered), this, &Freecell::gameTypeChanged);
+ connect(m_reservesOption, static_cast<void (KSelectAction::*)(int)>(&KSelectAction::triggered), this, &Freecell::gameTypeChanged);
+ connect(m_sequenceBuiltByOption, static_cast<void (KSelectAction::*)(int)>(&KSelectAction::triggered), this, &Freecell::gameTypeChanged);
+ connect(m_stacksOption, static_cast<void (KSelectAction::*)(int)>(&KSelectAction::triggered), this, &Freecell::gameTypeChanged);
+ connect(m_decksOption, static_cast<void (KSelectAction::*)(int)>(&KSelectAction::triggered), this, &Freecell::gameTypeChanged);
+#endif
+}
+
+
+void Freecell::setSavedOptions()
+{
+ Settings::setFreecellEmptyStackFill( m_emptyStackFill );
+ Settings::setFreecellSequenceBuiltBy( m_sequenceBuiltBy );
+ Settings::setFreecellReserves( m_reserves );
+ Settings::setFreecellStacks( m_stacks );
+ Settings::setFreecellDecks( m_decks );
+}
+
+
+void Freecell::getSavedOptions()
+{
+ m_emptyStackFill = Settings::freecellEmptyStackFill();
+ m_sequenceBuiltBy = Settings::freecellSequenceBuiltBy();
+ m_reserves = Settings::freecellReserves();
+ m_stacks = Settings::freecellStacks();
+ m_decks = Settings::freecellDecks();
+
+ matchVariant();
+
+ m_emptyStackFillOption->setCurrentItem( m_emptyStackFill );
+ m_sequenceBuiltByOption->setCurrentItem( m_sequenceBuiltBy );
+ m_reservesOption->setCurrentItem( m_reserves );
+ m_stacksOption->setCurrentItem( m_stacks );
+ m_decksOption->setCurrentItem( m_decks );
+}
+
+
+void Freecell::mapOldId(int id)
+{
+ switch (id) {
+
+ case DealerInfo::FreecellBakersId :
+ setOptions(0);
+ break;
+ case DealerInfo::FreecellEightOffId :
+ setOptions(1);
+ break;
+ case DealerInfo::FreecellForeId :
+ setOptions(2);
+ break;
+ case DealerInfo::FreecellId :
+ setOptions(3);
+ break;
+ case DealerInfo::FreecellSeahavenId :
+ setOptions(4);
+ break;
+ case DealerInfo::FreecellCustomId :
+ setOptions(5);
+ break;
+ default:
+ // Do nothing.
+ break;
+ }
+}
+
+
+int Freecell::oldId() const
+{
+ switch (m_variation) {
+ case 0 :
+ return DealerInfo::FreecellBakersId;
+ case 1 :
+ return DealerInfo::FreecellEightOffId;
+ case 2 :
+ return DealerInfo::FreecellForeId;
+ case 3 :
+ return DealerInfo::FreecellId;
+ case 4 :
+ return DealerInfo::FreecellSeahavenId;
+ default :
+ return DealerInfo::FreecellCustomId;
+ }
+}
+
+
+void Freecell::setOptions(int variation)
+{
+ if ( variation != m_variation )
+ {
+ m_variation = variation;
+ m_emptyStackFill = 0;
+ m_sequenceBuiltBy = 0;
+ m_reserves = 4;
+ m_stacks = 2;
+ m_decks = 0;
+
+ switch (m_variation) {
+ case 0 :
+ m_sequenceBuiltBy = 1;
+ break;
+ case 1 :
+ m_emptyStackFill = 1;
+ m_sequenceBuiltBy = 1;
+ m_reserves = 8;
+ break;
+ case 2 :
+ m_emptyStackFill = 1;
+ break;
+ case 3 :
+ break;
+ case 4 :
+ m_emptyStackFill = 1;
+ m_sequenceBuiltBy = 1;
+ m_stacks = 4;
+ break;
+ case 5 :
+ m_emptyStackFill = 2;
+ m_sequenceBuiltBy = 2;
+ break;
+ }
+
+ m_emptyStackFillOption->setCurrentItem( m_emptyStackFill );
+ m_sequenceBuiltByOption->setCurrentItem( m_sequenceBuiltBy );
+ m_reservesOption->setCurrentItem( m_reserves );
+ m_stacksOption->setCurrentItem( m_stacks );
+ m_decksOption->setCurrentItem( m_decks );
+ }
+}
diff --git a/src/freecell.h b/src/freecell.h
index 059fd71..f64b8b4 100644
--- a/src/freecell.h
+++ b/src/freecell.h
@@ -41,6 +41,7 @@
#include "dealer.h"
#include "hint.h"
+class KSelectAction;
class Freecell : public DealerScene
{
@@ -49,13 +50,18 @@ class Freecell : public DealerScene
public:
explicit Freecell( const DealerInfo * di );
void initialize() override;
+ void mapOldId(int id) override;
+ int oldId() const override;
+ QList<QAction*> configActions() const override;
protected:
bool checkAdd(const PatPile * pile, const QList<KCard*> & oldCards, const QList<KCard*> & newCards) const override;
bool checkRemove(const PatPile * pile, const QList<KCard*> & cards) const override;
void cardsDroppedOnPile( const QList<KCard*> & cards, KCardPile * pile ) override;
void restart( const QList<KCard*> & cards ) override;
- QList<MoveHint> getHints() override;
+
+private Q_SLOTS:
+ void gameTypeChanged();
protected Q_SLOTS:
bool tryAutomaticMove( KCard * c ) override;
@@ -63,10 +69,34 @@ protected Q_SLOTS:
private:
bool canPutStore( const KCardPile * pile, const QList<KCard*> & cards ) const;
+ void configOptions();
+ void setOptions(int v);
+ void getSavedOptions();
+ void setSavedOptions();
+ void matchVariant();
+
virtual QString solverFormat() const;
- PatPile* store[8];
- PatPile* freecell[4];
- PatPile* target[4];
+ PatPile* store[12];
+ PatPile* freecell[8];
+ PatPile* target[12];
+
+ KSelectAction *options;
+ int m_variation;
+
+ KSelectAction *m_emptyStackFillOption;
+ int m_emptyStackFill;
+
+ KSelectAction *m_sequenceBuiltByOption;
+ int m_sequenceBuiltBy;
+
+ KSelectAction *m_reservesOption;
+ int m_reserves;
+
+ KSelectAction *m_stacksOption;
+ int m_stacks;
+
+ KSelectAction *m_decksOption;
+ int m_decks;
friend class FreecellSolver;
};
diff --git a/src/kpat.kcfg b/src/kpat.kcfg
index 15c951b..9fd9d07 100644
--- a/src/kpat.kcfg
+++ b/src/kpat.kcfg
@@ -35,6 +35,21 @@
<entry name="FreecellSolverIterationsLimit" key="FreecellSolverIterationsLimit" type="Int">
<default>200000</default>
</entry>
+ <entry name="FreecellEmptyStackFill" key="FreecellEmptyStackFill" type="Int">
+ <default>0</default>
+ </entry>
+ <entry name="FreecellSequenceBuiltBy" key="FreecellSequenceBuiltBy" type="Int">
+ <default>0</default>
+ </entry>
+ <entry name="FreecellReserves" key="FreecellReserves" type="Int">
+ <default>4</default>
+ </entry>
+ <entry name="FreecellStacks" key="FreecellStacks" type="Int">
+ <default>2</default>
+ </entry>
+ <entry name="FreecellDecks" key="FreecellDecks" type="Int">
+ <default>0</default>
+ </entry>
<entry name="SimpleSimonSolverIterationsLimit" key="SimpleSimonSolverIterationsLimit" type="Int">
<default>200000</default>
</entry>
diff --git a/src/patsolve/abstract_fc_solve_solver.h b/src/patsolve/abstract_fc_solve_solver.h
index e0878de..73a883d 100644
--- a/src/patsolve/abstract_fc_solve_solver.h
+++ b/src/patsolve/abstract_fc_solve_solver.h
@@ -21,7 +21,7 @@
// own
#include "patsolve.h"
-struct FcSolveSolver : public Solver<13>
+struct FcSolveSolver : public Solver<20>
{
public:
FcSolveSolver();
diff --git a/src/patsolve/freecellsolver.cpp b/src/patsolve/freecellsolver.cpp
index 56a35b2..e879e4c 100644
--- a/src/patsolve/freecellsolver.cpp
+++ b/src/patsolve/freecellsolver.cpp
@@ -21,6 +21,7 @@
// own
#include "patsolve-config.h"
#include "../freecell.h"
+#include "../settings.h"
// freecell-solver
#include "freecell-solver/fcs_user.h"
#include "freecell-solver/fcs_cl.h"
@@ -28,6 +29,12 @@
#include <cstdlib>
#include <cstring>
+int m_reserves = Settings::freecellReserves();
+int m_stacks = Settings::freecellStacks() + 6;
+int m_decks = Settings::freecellDecks() + 1;
+int m_emptyStackFill = Settings::freecellEmptyStackFill();
+int m_sequenceBuiltBy = Settings::freecellSequenceBuiltBy();
+
const int CHUNKSIZE = 100;
const long int MAX_ITERS_LIMIT = 200000;
@@ -202,7 +209,7 @@ int FreecellSolver::good_automove(int o, int r)
return true;
}
- for (int foundation_idx = 0; foundation_idx < 4; ++foundation_idx) {
+ for (int foundation_idx = 0; foundation_idx < 4 * m_decks; ++foundation_idx) {
KCard *c = deal->target[foundation_idx]->topCard();
if (c) {
O[translateSuit( c->suit() ) >> 4] = c->rank();
@@ -210,7 +217,7 @@ int FreecellSolver::good_automove(int o, int r)
}
/* Check the Out piles of opposite color. */
- for (int i = 1 - (o & 1); i < 4; i += 2) {
+ for (int i = 1 - (o & 1); i < 4 * m_decks; i += 2) {
if (O[i] < r - 1) {
#if 1 /* Raymond's Rule */
@@ -221,7 +228,7 @@ int FreecellSolver::good_automove(int o, int r)
the loop variable i. We return here and never
make it back to the outer loop. */
- for (i = 1 - (o & 1); i < 4; i += 2) {
+ for (i = 1 - (o & 1); i < 4 * m_decks; i += 2) {
if (O[i] < r - 2) {
return false;
}
@@ -250,7 +257,7 @@ int FreecellSolver::get_possible_moves(int *a, int *numout)
int n = 0;
mp = Possible;
- for (w = 0; w < Nwpiles + Ntpiles; ++w) {
+ for (w = 0; w < m_stacks + m_reserves; ++w) {
if (Wlen[w] > 0) {
card = *Wp[w];
int out_suit = SUIT(card);
@@ -316,12 +323,24 @@ void FreecellSolver::setFcSolverGameParams()
*
* Shlomi Fish
* */
- freecell_solver_user_set_num_freecells(solver_instance,4);
- freecell_solver_user_set_num_stacks(solver_instance,8);
- freecell_solver_user_set_num_decks(solver_instance,1);
- freecell_solver_user_set_sequences_are_built_by_type(solver_instance, FCS_SEQ_BUILT_BY_ALTERNATE_COLOR);
freecell_solver_user_set_sequence_move(solver_instance, 0);
- freecell_solver_user_set_empty_stacks_filled_by(solver_instance, FCS_ES_FILLED_BY_ANY_CARD);
+
+ m_reserves = Settings::freecellReserves();
+ freecell_solver_user_set_num_freecells(solver_instance, m_reserves);
+
+ m_stacks = Settings::freecellStacks() + 6;
+ freecell_solver_user_set_num_stacks(solver_instance, m_stacks);
+
+ m_decks = Settings::freecellDecks() + 1;
+ freecell_solver_user_set_num_decks(solver_instance, m_decks);
+
+ //FCS_ES_FILLED_BY_ANY_CARD = 0, FCS_ES_FILLED_BY_KINGS_ONLY = 1,FCS_ES_FILLED_BY_NONE = 2
+ m_emptyStackFill = Settings::freecellEmptyStackFill();
+ freecell_solver_user_set_empty_stacks_filled_by(solver_instance, m_emptyStackFill);
+
+ //FCS_SEQ_BUILT_BY_ALTERNATE_COLOR = 0, FCS_SEQ_BUILT_BY_SUIT = 1, FCS_SEQ_BUILT_BY_RANK = 2
+ m_sequenceBuiltBy = Settings::freecellSequenceBuiltBy();
+ freecell_solver_user_set_sequences_are_built_by_type(solver_instance, m_sequenceBuiltBy);
}
#if 0
void FreecellSolver::unpack_cluster( unsigned int k )
@@ -409,7 +428,7 @@ MoveHint FreecellSolver::translateMove( const MOVE &m )
{
PatPile *target = nullptr;
PatPile *empty = nullptr;
- for (int i = 0; i < 4; ++i) {
+ for (int i = 0; i < 4 * m_decks; ++i) {
KCard *c = deal->target[i]->topCard();
if (c) {
if ( c->suit() == card->suit() )
@@ -431,17 +450,17 @@ MoveHint FreecellSolver::translateMove( const MOVE &m )
// this is tricky as we need to want to build the "meta moves"
PatPile *frompile = nullptr;
- if ( m.from < 8 )
+ if ( m.from < m_stacks )
frompile = deal->store[m.from];
else
- frompile = deal->freecell[m.from-8];
+ frompile = deal->freecell[m.from - m_stacks];
KCard *card = frompile->at( frompile->count() - m.card_index - 1);
if ( m.totype == O_Type )
{
PatPile *target = nullptr;
PatPile *empty = nullptr;
- for (int i = 0; i < 4; ++i) {
+ for (int i = 0; i < 4 * m_decks; ++i) {
KCard *c = deal->target[i]->topCard();
if (c) {
if ( c->suit() == card->suit() )
@@ -457,10 +476,10 @@ MoveHint FreecellSolver::translateMove( const MOVE &m )
return MoveHint( card, target, m.pri );
} else {
PatPile *target = nullptr;
- if ( m.to < 8 )
+ if ( m.to < m_stacks )
target = deal->store[m.to];
else
- target = deal->freecell[m.to-8];
+ target = deal->freecell[m.to - m_stacks];
return MoveHint( card, target, m.pri );
}
@@ -495,32 +514,32 @@ void FreecellSolver::translate_layout()
/* Read the workspace. */
int total = 0;
- for ( int w = 0; w < 8; ++w ) {
- int i = translate_pile(deal->store[w], W[w], 52);
+ for ( int w = 0; w < m_stacks; ++w ) {
+ int i = translate_pile(deal->store[w], W[w], 52 * m_decks);
Wp[w] = &W[w][i - 1];
Wlen[w] = i;
total += i;
- if (w == Nwpiles) {
+ if (w == m_stacks) {
break;
}
}
/* Temp cells may have some cards too. */
- for (int w = 0; w < Ntpiles; ++w)
+ for (int w = 0; w < m_reserves; ++w)
{
- int i = translate_pile( deal->freecell[w], W[w+Nwpiles], 52 );
- Wp[w+Nwpiles] = &W[w+Nwpiles][i-1];
- Wlen[w+Nwpiles] = i;
+ int i = translate_pile( deal->freecell[w], W[w+m_stacks], 52 * m_decks );
+ Wp[w+m_stacks] = &W[w+m_stacks][i-1];
+ Wlen[w+m_stacks] = i;
total += i;
}
/* Output piles, if any. */
- for (int i = 0; i < 4; ++i) {
+ for (int i = 0; i < 4 * m_decks; ++i) {
O[i] = NONE;
}
- if (total != 52) {
- for (int i = 0; i < 4; ++i) {
+ if (total != 52 * m_decks) {
+ for (int i = 0; i < 4 * m_decks; ++i) {
KCard *c = deal->target[i]->topCard();
if (c) {
O[translateSuit( c->suit() ) >> 4] = c->rank();
diff --git a/src/patsolve/freecellsolver.h b/src/patsolve/freecellsolver.h
index 3a6a7c7..a327b79 100644
--- a/src/patsolve/freecellsolver.h
+++ b/src/patsolve/freecellsolver.h
@@ -64,7 +64,7 @@ public:
static int Xparam[];
#endif
- card_t O[4]; /* output piles store only the rank or NONE */
+ card_t O[12]; /* output piles store only the rank or NONE */
const Freecell *deal;
};
diff --git a/src/patsolve/patsolve.cpp b/src/patsolve/patsolve.cpp
index f649adf..0f4be58 100644
--- a/src/patsolve/patsolve.cpp
+++ b/src/patsolve/patsolve.cpp
@@ -990,4 +990,5 @@ template class Solver<34>;
template class Solver<15>;
template class Solver<7>;
template class Solver<13>;
+template class Solver<20>;
template class Solver<Nwpiles + Ntpiles>;
More information about the kde-doc-english
mailing list