Commit 5595a803 authored by Michael Lang's avatar Michael Lang Committed by Albert Astals Cid
Browse files

Added Castle game type with several variation presets

-Beleaguered Castle
-Citadel
-Exiled Kings
-Streets and Alleys
-Siegecraft
-Stronghold
-Other custom varations via settings
parent 411107e3
......@@ -636,6 +636,58 @@ the last card is ready to be moved to a foundation.
</sect2>
<sect2 id="castle">
<title>Castle</title>
<para><indexterm><primary>Castle</primary></indexterm>
Castle is a family of patience or solitaire card games typically played with a deck of 52 playing cards. It is sometimes described as "Freecell without cells" because its game play is similar but without extra empty spaces to maneuver in most variations.
</para>
<para>
The object of the game is to build all the cards onto the four foundations by suit, each from ace to king
</para>
<para>
In the playing piles you have to build descending sequences, regardless of suit.
You can only move one card that lays on top of a pile.
</para>
<variablelist>
<varlistentry><term>Variations:</term>
<listitem>
<para>
- Beleaguered Castle. Aces are dealt to the foundations. Any card can fill empty spaces.
</para>
<para>
- Citadel is like Beleaguered Castle, but matching cards are moved to the foundation during the deal, leaving uneven piles.
</para>
<para>
- Exiled Kings is like Citadel, but only kings can fill empty spaces.
</para>
<para>
- Siegecraft is like Beleaguered Castle, but with one free cell.
</para>
<para>
- Streets and Alleys is like Beleaguered Castle, but aces are included in shuffling.
</para>
<para>
- Stronghold is like Streets and Alleys, but with one free cell.
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
To solve this game it is recommended to build evenly on the foundations. Try to create empty piles with can be used to faciliate longer moves to free up buried cards.
</para>
</sect2>
</sect1>
</chapter>
......
......@@ -2,6 +2,7 @@ set( kpat_previews
1.png
2.png
3.png
4.png
5.png
7.png
8.png
......
......@@ -28,6 +28,8 @@ set(kpat_SRCS ${libfcs_SRCS}
bakersdozen.cpp
patsolve/bakersdozensolver.cpp
castle
patsolve/castlesolver.cpp
clock.cpp
patsolve/clocksolver.cpp
fortyeight.cpp
......
This diff is collapsed.
/*
* Copyright 2021 Michael Lang <criticaltemp@protonmail.com>
*
* 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 2 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/>.
*/
#ifndef CASTLE_H
#define CASTLE_H
// own
#include "dealer.h"
#include "hint.h"
class KSelectAction;
class Castle : public DealerScene
{
Q_OBJECT
public:
explicit Castle( 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;
private Q_SLOTS:
void gameTypeChanged();
protected Q_SLOTS:
bool tryAutomaticMove( KCard * c ) override;
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[10];
PatPile* freecell[4];
PatPile* target[8];
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_stackFaceupOption;
int m_stackFaceup;
KSelectAction *m_decksOption;
int m_decks;
KSelectAction *m_foundationOption;
int m_foundation;
KSelectAction *m_layoutOption;
int m_layout;
friend class CastleSolver;
};
#endif
......@@ -57,6 +57,7 @@ public:
GrandfatherId = 1,
AcesUpId = 2,
FreecellGeneralId = 3,
CastleGeneralId = 4,
Mod3Id = 5,
GypsyId = 7,
FortyAndEightId = 8,
......@@ -78,10 +79,17 @@ public:
BakersDozenCustomId = 24,
FreecellId = 30,
FreecellBakersId = 31,
FreecellEightOffId = 32,
FreecellEightOffId = 32,
FreecellForeId = 33,
FreecellSeahavenId = 34,
FreecellCustomId = 39
FreecellCustomId = 39,
CastleBeleagueredId = 40,
CastleCitadelId = 41,
CastleExiledKingsId = 42,
CastleStreetAlleyId = 43,
CastleSiegecraftId = 44,
CastleStrongholdId = 45,
CastleCustomId = 49
};
DealerInfo( const QByteArray & untranslatedBaseName, int baseId );
......
......@@ -65,5 +65,29 @@
<entry name="BakersDozenSequenceBuiltBy" key="BakersDozenSequenceBuiltBy" type="Int">
<default>2</default>
</entry>
<entry name="CastleSolverIterationsLimit" key="CastleSolverIterationsLimit" type="Int">
<default>200000</default>
</entry>
<entry name="CastleEmptyStackFill" key="CastleEmptyStackFill" type="Int">
<default>0</default>
</entry>
<entry name="CastleSequenceBuiltBy" key="CastleSequenceBuiltBy" type="Int">
<default>2</default>
</entry>
<entry name="CastleReserves" key="CastleReserves" type="Int">
<default>0</default>
</entry>
<entry name="CastleStacks" key="CastleStacks" type="Int">
<default>8</default>
</entry>
<entry name="CastleStackFaceup" key="CastleStackFaceup" type="Int">
<default>1</default>
</entry>
<entry name="CastleFoundation" key="CastleFoundation" type="Int">
<default>1</default>
</entry>
<entry name="CastleLayout" key="CastleLayout" type="Int">
<default>0</default>
</entry>
</group>
</kcfg>
/*
* Copyright (C) 1998-2002 Tom Holroyd <tomh@kurage.nimh.nih.gov>
* Copyright (C) 2006-2009 Stephan Kulow <coolo@kde.org>
*
* 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 2 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 "castlesolver.h"
// own
#include "patsolve-config.h"
#include "../castle.h"
#include "../settings.h"
// freecell-solver
#include "freecell-solver/fcs_user.h"
#include "freecell-solver/fcs_cl.h"
// St
#include <cstdlib>
#include <cstring>
namespace {
int m_reserves = Settings::castleReserves();
int m_stacks = Settings::castleStacks();
int m_decks = 1;
int m_emptyStackFill = Settings::castleEmptyStackFill();
int m_sequenceBuiltBy = Settings::castleSequenceBuiltBy();
}
/* Automove logic. Freecell games must avoid certain types of automoves. */
int CastleSolver::good_automove(int o, int r)
{
if (r <= 2) {
return true;
}
if (m_sequenceBuiltBy == 1) {
return true;
}
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();
}
}
/* Check the Out piles of opposite color. */
for (int i = 1 - (o & 1); i < 4 * m_decks; i += 2) {
if (O[i] < r - 1) {
#if 1 /* Raymond's Rule */
/* Not all the N-1's of opposite color are out
yet. We can still make an automove if either
both N-2's are out or the other same color N-3
is out (Raymond's rule). Note the re-use of
the loop variable i. We return here and never
make it back to the outer loop. */
for (i = 1 - (o & 1); i < 4 * m_decks; i += 2) {
if (O[i] < r - 2) {
return false;
}
}
if (O[(o + 2) & 3] < r - 3) {
return false;
}
return true;
#else /* Horne's Rule */
return false;
#endif
}
}
return true;
}
int CastleSolver::get_possible_moves(int *a, int *numout)
{
int w;
card_t card;
MOVE *mp;
/* Check for moves from W to O. */
int n = 0;
mp = Possible;
for (w = 0; w < m_stacks + m_reserves; ++w) {
if (Wlen[w] > 0) {
card = *Wp[w];
int out_suit = SUIT(card);
const bool empty = (O[out_suit] == NONE);
if ((empty && (RANK(card) == PS_ACE)) ||
(!empty && (RANK(card) == O[out_suit] + 1))) {
mp->is_fcs = false;
mp->card_index = 0;
mp->from = w;
mp->to = out_suit;
mp->totype = O_Type;
mp->turn_index = -1;
mp->pri = 0; /* unused */
n++;
mp++;
/* If it's an automove, just do it. */
if (good_automove(out_suit, RANK(card))) {
*a = true;
mp[-1].pri = 127;
if (n != 1) {
Possible[0] = mp[-1];
return (*numout = 1);
}
return (*numout = n);
}
}
}
}
return (*numout = 0);
}
#define CMD_LINE_ARGS_NUM 2
static const char * freecell_solver_cmd_line_args[CMD_LINE_ARGS_NUM] =
{
#ifdef WITH_FCS_SOFT_SUSPEND
"--load-config", "video-editing"
#else
"--load-config", "slick-rock"
#endif
};
int CastleSolver::get_cmd_line_arg_count()
{
return CMD_LINE_ARGS_NUM;
}
const char * * CastleSolver::get_cmd_line_args()
{
return freecell_solver_cmd_line_args;
}
void CastleSolver::setFcSolverGameParams()
{
/*
* I'm using the more standard interface instead of the depracated
* user_set_game one. I'd like that each function will have its
* own dedicated purpose.
*
* Shlomi Fish
* */
freecell_solver_user_set_sequence_move(solver_instance, 0);
m_reserves = Settings::castleReserves();
freecell_solver_user_set_num_freecells(solver_instance, m_reserves);
m_stacks = Settings::castleStacks();
freecell_solver_user_set_num_stacks(solver_instance, m_stacks);
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::castleEmptyStackFill();
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::castleSequenceBuiltBy();
freecell_solver_user_set_sequences_are_built_by_type(solver_instance, m_sequenceBuiltBy);
}
CastleSolver::CastleSolver(const Castle *dealer)
: FcSolveSolver()
{
deal = dealer;
}
MoveHint CastleSolver::translateMove( const MOVE &m )
{
if (m.is_fcs)
{
fcs_move_t move = m.fcs;
int cards = fcs_move_get_num_cards_in_seq(move);
PatPile *from = nullptr;
PatPile *to = nullptr;
switch(fcs_move_get_type(move))
{
case FCS_MOVE_TYPE_STACK_TO_STACK:
from = deal->store[fcs_move_get_src_stack(move)];
to = deal->store[fcs_move_get_dest_stack(move)];
break;
case FCS_MOVE_TYPE_FREECELL_TO_STACK:
from = deal->freecell[fcs_move_get_src_freecell(move)];
to = deal->store[fcs_move_get_dest_stack(move)];
cards = 1;
break;
case FCS_MOVE_TYPE_FREECELL_TO_FREECELL:
from = deal->freecell[fcs_move_get_src_freecell(move)];
to = deal->freecell[fcs_move_get_dest_freecell(move)];
cards = 1;
break;
case FCS_MOVE_TYPE_STACK_TO_FREECELL:
from = deal->store[fcs_move_get_src_stack(move)];
to = deal->freecell[fcs_move_get_dest_freecell(move)];
cards = 1;
break;
case FCS_MOVE_TYPE_STACK_TO_FOUNDATION:
from = deal->store[fcs_move_get_src_stack(move)];
cards = 1;
to = nullptr;
break;
case FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION:
from = deal->freecell[fcs_move_get_src_freecell(move)];
cards = 1;
to = nullptr;
}
Q_ASSERT(from);
Q_ASSERT(cards <= from->cards().count());
Q_ASSERT(to || cards == 1);
KCard *card = from->cards()[from->cards().count() - cards];
if (!to)
{
PatPile *target = nullptr;
PatPile *empty = nullptr;
for (int i = 0; i < 4 * m_decks; ++i) {
KCard *c = deal->target[i]->topCard();
if (c) {
if ( c->suit() == card->suit() )
{
target = deal->target[i];
break;
}
} else if ( !empty )
empty = deal->target[i];
}
to = target ? target : empty;
}
Q_ASSERT(to);
return MoveHint(card, to, 0);
}
else
{
// this is tricky as we need to want to build the "meta moves"
PatPile *frompile = nullptr;
if ( m.from < m_stacks )
frompile = deal->store[m.from];
else
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 * m_decks; ++i) {
KCard *c = deal->target[i]->topCard();
if (c) {
if ( c->suit() == card->suit() )
{
target = deal->target[i];
break;
}
} else if ( !empty )
empty = deal->target[i];
}
if ( !target )
target = empty;
return MoveHint( card, target, m.pri );
} else {
PatPile *target = nullptr;
if ( m.to < m_stacks )
target = deal->store[m.to];
else
target = deal->freecell[m.to - m_stacks];
return MoveHint( card, target, m.pri );
}
}
}
void CastleSolver::translate_layout()
{
strcpy(board_as_string, deal->solverFormat().toLatin1().constData());
make_solver_instance_ready();
/* Read the workspace. */
int total = 0;
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 == m_stacks) {
break;
}
}
/* Temp cells may have some cards too. */
for (int w = 0; w < m_reserves; ++w)
{
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 * m_decks; ++i) {
O[i] = NONE;
}
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();
total += c->rank();
}
}
}
}
/*
* Copyright (C) 1998-2002 Tom Holroyd <tomh@kurage.nimh.nih.gov>
* Copyright (C) 2006-2009 Stephan Kulow <coolo@kde.org>
*
* 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 2 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/>.
*/
#ifndef CASTLESOLVER_H
#define CASTLESOLVER_H
// own
#include "abstract_fc_solve_solver.h"
constexpr auto Nwpiles = 8;
constexpr auto Ntpiles = 4;
class Castle;
class CastleSolver : public FcSolveSolver
{
public:
explicit CastleSolver(const Castle *dealer);
int good_automove(int o, int r);
int get_possible_moves(int *a, int *numout) override;
void translate_layout() override;
MoveHint translateMove(const MOVE &m) override;
void setFcSolverGameParams() override;