summaryrefslogtreecommitdiffstats
path: root/kpat
diff options
context:
space:
mode:
Diffstat (limited to 'kpat')
-rw-r--r--kpat/AUTHORS6
-rw-r--r--kpat/CHANGES150
-rw-r--r--kpat/Makefile.am30
-rw-r--r--kpat/README41
-rw-r--r--kpat/README.tkcTrump8
-rw-r--r--kpat/TODO42
-rw-r--r--kpat/card.cpp380
-rw-r--r--kpat/card.h131
-rw-r--r--kpat/cardmaps.cpp248
-rw-r--r--kpat/cardmaps.h59
-rw-r--r--kpat/clock.cpp91
-rw-r--r--kpat/clock.h24
-rw-r--r--kpat/computation.cpp120
-rw-r--r--kpat/computation.h53
-rw-r--r--kpat/copyright.h154
-rw-r--r--kpat/dealer.cpp1455
-rw-r--r--kpat/dealer.h228
-rw-r--r--kpat/deck.cpp154
-rw-r--r--kpat/deck.h46
-rw-r--r--kpat/fortyeight.cpp205
-rw-r--r--kpat/fortyeight.h46
-rw-r--r--kpat/freecell-solver/CREDITS57
-rw-r--r--kpat/freecell-solver/INSTALL70
-rw-r--r--kpat/freecell-solver/Makefile.am5
-rw-r--r--kpat/freecell-solver/Makefile.lite94
-rw-r--r--kpat/freecell-solver/README105
-rw-r--r--kpat/freecell-solver/USAGE518
-rw-r--r--kpat/freecell-solver/alloc.c127
-rw-r--r--kpat/freecell-solver/alloc.h86
-rw-r--r--kpat/freecell-solver/app_str.c74
-rw-r--r--kpat/freecell-solver/app_str.h39
-rw-r--r--kpat/freecell-solver/caas.c629
-rw-r--r--kpat/freecell-solver/caas.h28
-rw-r--r--kpat/freecell-solver/card.c286
-rw-r--r--kpat/freecell-solver/card.h100
-rw-r--r--kpat/freecell-solver/cl_chop.c245
-rw-r--r--kpat/freecell-solver/cl_chop.h19
-rw-r--r--kpat/freecell-solver/cmd_line.c964
-rw-r--r--kpat/freecell-solver/fcs.h797
-rw-r--r--kpat/freecell-solver/fcs_cl.h65
-rw-r--r--kpat/freecell-solver/fcs_config.h95
-rw-r--r--kpat/freecell-solver/fcs_dm.c146
-rw-r--r--kpat/freecell-solver/fcs_dm.h49
-rw-r--r--kpat/freecell-solver/fcs_enums.h77
-rw-r--r--kpat/freecell-solver/fcs_hash.c291
-rw-r--r--kpat/freecell-solver/fcs_hash.h102
-rw-r--r--kpat/freecell-solver/fcs_isa.c88
-rw-r--r--kpat/freecell-solver/fcs_isa.h56
-rw-r--r--kpat/freecell-solver/fcs_move.h122
-rw-r--r--kpat/freecell-solver/fcs_user.h275
-rw-r--r--kpat/freecell-solver/freecell.c2433
-rw-r--r--kpat/freecell-solver/inline.h20
-rw-r--r--kpat/freecell-solver/intrface.c1764
-rw-r--r--kpat/freecell-solver/jhjtypes.h25
-rw-r--r--kpat/freecell-solver/lib.c1244
-rw-r--r--kpat/freecell-solver/lookup2.c119
-rw-r--r--kpat/freecell-solver/lookup2.h13
-rw-r--r--kpat/freecell-solver/main.c859
-rw-r--r--kpat/freecell-solver/move.c531
-rw-r--r--kpat/freecell-solver/move.h172
-rw-r--r--kpat/freecell-solver/ms_ca.h33
-rw-r--r--kpat/freecell-solver/pqueue.c173
-rw-r--r--kpat/freecell-solver/pqueue.h71
-rw-r--r--kpat/freecell-solver/prefix.h4
-rw-r--r--kpat/freecell-solver/preset.c637
-rw-r--r--kpat/freecell-solver/preset.h62
-rw-r--r--kpat/freecell-solver/rand.c30
-rw-r--r--kpat/freecell-solver/rand.h49
-rw-r--r--kpat/freecell-solver/scans.c1170
-rw-r--r--kpat/freecell-solver/simpsim.c1716
-rw-r--r--kpat/freecell-solver/state.c1114
-rw-r--r--kpat/freecell-solver/state.h660
-rw-r--r--kpat/freecell-solver/test_arr.h136
-rw-r--r--kpat/freecell-solver/tests.h307
-rw-r--r--kpat/freecell.cpp854
-rw-r--r--kpat/freecell.h95
-rw-r--r--kpat/gamestats.ui272
-rw-r--r--kpat/gamestatsimpl.cpp57
-rw-r--r--kpat/gamestatsimpl.h16
-rw-r--r--kpat/golf.cpp169
-rw-r--r--kpat/golf.h45
-rw-r--r--kpat/grandf.cpp227
-rw-r--r--kpat/grandf.h65
-rw-r--r--kpat/green.pngbin0 -> 144 bytes
-rw-r--r--kpat/gypsy.cpp117
-rw-r--r--kpat/gypsy.h34
-rw-r--r--kpat/hint.h28
-rw-r--r--kpat/icons/Makefile.am6
-rw-r--r--kpat/icons/hi128-app-kpat.pngbin0 -> 10455 bytes
-rw-r--r--kpat/icons/hi16-app-kpat.pngbin0 -> 721 bytes
-rw-r--r--kpat/icons/hi22-app-kpat.pngbin0 -> 3715 bytes
-rw-r--r--kpat/icons/hi32-app-kpat.pngbin0 -> 1858 bytes
-rw-r--r--kpat/icons/hi48-app-kpat.pngbin0 -> 3308 bytes
-rw-r--r--kpat/icons/hi64-app-kpat.pngbin0 -> 4529 bytes
-rw-r--r--kpat/icons/kpat-lq.pngbin0 -> 658 bytes
-rw-r--r--kpat/idiot.cpp234
-rw-r--r--kpat/idiot.h59
-rw-r--r--kpat/kings.cpp132
-rw-r--r--kpat/kings.h22
-rw-r--r--kpat/klondike.cpp495
-rw-r--r--kpat/klondike.h71
-rw-r--r--kpat/kpat.desktop116
-rw-r--r--kpat/kpatui.rc40
-rw-r--r--kpat/main.cpp73
-rw-r--r--kpat/mod3.cpp312
-rw-r--r--kpat/mod3.h62
-rw-r--r--kpat/napoleon.cpp204
-rw-r--r--kpat/napoleon.h56
-rw-r--r--kpat/pile.cpp463
-rw-r--r--kpat/pile.h154
-rw-r--r--kpat/pile_algorithms.cpp69
-rw-r--r--kpat/pwidget.cpp560
-rw-r--r--kpat/pwidget.h90
-rw-r--r--kpat/simon.cpp156
-rw-r--r--kpat/simon.h29
-rw-r--r--kpat/speeds.h26
-rw-r--r--kpat/spider.cpp484
-rw-r--r--kpat/spider.h71
-rw-r--r--kpat/version.h6
-rw-r--r--kpat/yukon.cpp130
-rw-r--r--kpat/yukon.h23
121 files changed, 28744 insertions, 0 deletions
diff --git a/kpat/AUTHORS b/kpat/AUTHORS
new file mode 100644
index 00000000..cc9f0a73
--- /dev/null
+++ b/kpat/AUTHORS
@@ -0,0 +1,6 @@
+Olav Tvete (original author)
+Matthias Ettrich <ettrich@kde.org>
+Mario Weilguni <mweilguni@kde.org>
+Rodolfo Borges <barrett@labma.ufrj.br>
+Peter H. Ruegg <kpat@incense.org>
+Stephan Kulow <coolo@kde.org> (current maintainer)
diff --git a/kpat/CHANGES b/kpat/CHANGES
new file mode 100644
index 00000000..2480f12e
--- /dev/null
+++ b/kpat/CHANGES
@@ -0,0 +1,150 @@
+2005-06-25 Inge Wallin <inge@lysator.liu.se>
+
+ Fix bug 131587: "cannot win this game" message just after
+ start of mod3 game
+ - Check for ace at the store in addition to empty
+
+2005-06-25 Inge Wallin <inge@lysator.liu.se>
+
+ Code cleaning
+ - Rename Card::Value --> Card::Rank
+ - Rename Card::Suites --> Card::Suit
+
+2005-02-18 Inge Wallin <inge@lysator.liu.se>
+
+ Bumped version number to 2.2.2 because of all the bug fixes
+ during the last months and the upcoming release of KDE 3.4.
+
+2005-01-07 Inge Wallin <inge@lysator.liu.se>
+
+ Fix bug 91317: Klondike (draw 3) incorrect unwinnable game message
+ - Fix criteria for lost game in klondike.
+
+ ---- CVS commit here ----
+
+ Fix bug 96531: KPat: Mod3 incorrect unwinnable game message
+ - Fix criteria for lost game in mod3.
+
+2004-11-10 Inge Wallin <inge@lysator.liu.se>
+
+ Continue the code cleaning
+ - card.h, card.cpp
+
+2004-11-04 Inge Wallin <inge@lysator.liu.se>
+
+ Fix the fix for 92002 below:
+ - Only ask for confirmation if the user actively aborts the game.
+
+ ---- CVS commit here ----
+
+ Start of a thorough code cleaning:
+ - Start with hide.h and dealer.cpp
+
+2004-10-31 Inge Wallin <inge@lysator.liu.se>
+
+ - fix wish 92002: Restart game button should have a confirmation dialog
+
+2004-10-15 Inge Wallin <inge@lysator.liu.se>
+
+ - fix bug 89276: kpat: make nag screen "you have lost this game" an option
+
+2004-10-14 Inge Wallin <inge@lysator.liu.se>
+
+ - fix bug 87451: crash in patience when selecting 'unknown' as game type
+ ---- CVS commit here ----
+
+ - fix bug 89755: Aces up move counter doesn't work (idiot.cpp)
+ - bump version to 2.2.1
+
+------------------ KDE 3.3.1 released here ---------------------
+
+since kpat-0.7.3 (18/Nov/1999) <kpat@incense.org>
+
+ - bugfix in idiot.cpp
+ - bugfixes in grandf.cpp
+ - added CTRL-R for new game
+
+1999-02-01 Mario Weilguni <mweilguni@kde.org>
+
+ * fixes to avoid many warning from egcs
+
+Sat May 2 13:49:46 1998 Mario Weilguni <mweilguni@kde.org>
+
+ * replaced all locale->translate with i18n
+
+kapt 0.7.1
+ - [Robert Williams] added getHelpMenu()
+ - [Robert Williams] added -caption "%c" to kpat.kdelnk
+
+since kpat-0.7 (11/Dec/1997)
+
+ - added "Mod3" game (Rodolfo)
+ - added "Freecell" game (Rodolfo)
+ - Deck class can now handle any multiple of 52 cards (Rodolfo)
+
+since kpat-0.6
+
+ - bugfix for "Grandfather"-hint (Rodolfo)
+ - new KDE FSSTND compliant
+ - depending on display colors sets either a
+ 16 color or high color icon and miniicon
+ - fixes for new KConfig
+
+since kpat-0.5
+
+ - fixed the bug in Napoleon (cards not completely drawn)
+ - added locale
+ - german translation
+
+since kpat-0.4
+
+ - major rewrite of pwidget.(cpp|h)
+ - KDE compliant menubar and dialogs
+ - kpat now stores preferences (type of game...)
+ - window title includes current game type
+ - misc. cleanups
+ - kpat uses now kfixedtopwidget (similar to ktoplevelwidget)
+ - toolbar added (though it does not have many buttons)
+ - uses KMsgBox instead of QMessageBox
+ - now kpat has a fixed size
+ - (hopefully) KDE compliant shortcut keys
+ - did a lot of reformatting the sourcecode to make
+ it more readable (had to many empty lines IMHO)
+ - pwidget does not decide which sizes to use for
+ the different type of games, now the games itself supply
+ a sizehint (will make it more extendible)
+ - HTML help
+ - animation on startup, since loading the cards may take
+ several seconds to finish on slow machines.
+ - a lot more not mentioned here...
+
+since kpat-0.3
+
+ - kpat is a KApplication now (Matthias)
+
+since kpat-0.2
+
+ - added "MicroSolitaire" game (Paul)
+ - removed "cheating" option from Klondike (Paul)
+ - set wholeColumn for Klondike (Paul)
+ - Changed Klondike accelerator to Ctrl+K (Paul)
+ - Added menu bar hot keys (Paul)
+
+since kpat-0.1
+
+ - changed shading colorGroup (Paul)
+ - fixed drawing of suit symbols (Paul)
+ - renamed "very easy" to "cheating" :) (Paul)
+ - fixed spurious core dump(Paul)
+ - handle mousePress outside cards (Paul)
+ - menu cleanup (Paul)
+
+since "patience"
+
+ - renamed the stuff kpat-0.1 (Matthias)
+ - improved drag'n'drop (no more weird jumping of cards) (Matthias)
+ - improved look (shaded borders around the cards and piles) (Matthias)
+ - hopefully improved cards-background (Matthias)
+ - "very easy"-option for klondike to give only one card. (Matthias)
+ - unset wholeColumn for klondike, but this all belongs into OPTIONS (Matthias)
+
diff --git a/kpat/Makefile.am b/kpat/Makefile.am
new file mode 100644
index 00000000..1243bf9f
--- /dev/null
+++ b/kpat/Makefile.am
@@ -0,0 +1,30 @@
+
+INCLUDES = -I$(top_srcdir)/libkdegames $(all_includes)
+
+bin_PROGRAMS = kpat
+
+kpat_SOURCES = main.cpp cardmaps.cpp card.cpp dealer.cpp \
+ pwidget.cpp pile.cpp deck.cpp pile_algorithms.cpp kings.cpp freecell.cpp klondike.cpp simon.cpp grandf.cpp \
+ mod3.cpp idiot.cpp napoleon.cpp computation.cpp gypsy.cpp fortyeight.cpp \
+ yukon.cpp clock.cpp golf.cpp spider.cpp \
+ gamestatsimpl.cpp \
+ gamestats.ui
+kpat_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+kpat_LDADD = $(LIB_KFILE) $(LIB_KDEGAMES) ./freecell-solver/libfcs.la
+kpat_DEPENDENCIES = $(LIB_KDEGAMES_DEP)
+
+METASOURCES = AUTO
+
+SUBDIRS = icons freecell-solver
+
+xdg_apps_DATA = kpat.desktop
+
+rcdir = $(kde_datadir)/kpat
+rc_DATA = kpatui.rc
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/kpat.pot
+
+bgdir = $(kde_datadir)/kpat/backgrounds
+bg_DATA = green.png
+
diff --git a/kpat/README b/kpat/README
new file mode 100644
index 00000000..72a2cd6a
--- /dev/null
+++ b/kpat/README
@@ -0,0 +1,41 @@
+Hello,
+
+this is the very first release of kpat, the KDE solitaire patience game.
+
+It is an almost unchanged "patience" from Paul Olav Tvete, who uploaded
+this stuff some months ago on www.troll.no.
+
+My changes are so far:
+
+ - renamed the stuff kpat-0.1
+ - improved drag'n'drop (no more weird jumping of cards)
+ - improved look (shaded borders around the cards and piles)
+ - hopefully improved cards-background
+ - "very easy"-option for klondike to give only one card.
+ - unset wholeColumn for klondike, but this all belongs into OPTIONS
+
+Anway, kpat is IMO already yet the best X-based solitaire-like game,
+thanks to Paul Olav Tvete and Qt :-)
+
+
+Greets,
+
+ Matthias
+
+
+
+----- original README (probably Paul):
+
+This is an implementation of patience (solitaire). It consists of a
+general class library and six games implemented using it.
+
+I wrote this program when learning Qt. There are a number of things I
+would have done differently now. One of the silliest is that all the
+intelligence lies in the cards (which are individual widgets, by the way)
+instead of in a Patience class.
+
+There are a lot of static variables. Don't try to instantiate more than one
+dealer at a time -- that way lies madness.
+
+The general base classes could doubtlessly have been a lot more general.
+
diff --git a/kpat/README.tkcTrump b/kpat/README.tkcTrump
new file mode 100644
index 00000000..93cbba95
--- /dev/null
+++ b/kpat/README.tkcTrump
@@ -0,0 +1,8 @@
+If you bought a binary of tkcTrump from theKompany and want to modify
+the source code to fit your needs, check out
+http://developer.kde.org/~coolo/tkcTrump-path.diff.bz2
+
+Please note: I do not support the code, but if you saw something in tkcTrump
+worth backporting, look at the patch.
+
+Stephan Kulow
diff --git a/kpat/TODO b/kpat/TODO
new file mode 100644
index 00000000..3f4e41c1
--- /dev/null
+++ b/kpat/TODO
@@ -0,0 +1,42 @@
+Cleaning:
+ type Dealer --> Patience
+ type Card::Value --> Card::Rank
+
+
+TODO:
+ - more options (wholeColumn, etc.)
+ kpat already supports a lot, but they aren't accessible via GUI yet
+ - implement support for nice (configurable) backgrounds, like
+ color gradients, background pixmaps (tiled, wallpaper, etc. ).
+ - give feedback in the statusbar if there is no move except draw
+ possible. Open a messagebox when no more moves are possible at all.
+ (ie. Game Over).
+ - separate the dealer and related classed into a library, so
+ that other card games can use it.
+
+DONE:
+ - preview on cards that are hidden but faceup (RMB)
+ - animation when a card moves back automatically
+ - nice animation when you win a game
+ - game numbers as in MS freecell (thanks Marcus)
+ - undo
+ - ambigious place - choose one
+ - flip animation
+ - dblclick to put cards directly onto the piles.
+ - more colorful cards (there are some nice with xpat2)
+ same cards as KPoker (shamelessly stolen :-)
+ - better scaling (what about a 640x480 resolution?)
+ - more options (different backgrounds)
+ - kpat still uses the WidgetAt function when dropping cards. That
+ means it is not possible to put the hot spot onto the middle of the
+ dragged cards what would be better.
+
+Solutions:
+#1 Wilson Callan
+3a 32 7b 3c 37 37 b7 8b 87 48
+82 a8 4a 34 57 54 85 8d 87 c7
+d7 b8 38 23 28 32 6b 6c 78 a3
+73 7a 7c 74 c7 67 63 56 8h b8
+5b 51 b5 24 25 6h 6h 24 26 a4
+37 2a 8h 4h 1h 17 1h 1b 8h 4h
+4b 4c 4d a2 42 46 3h 7h 13
diff --git a/kpat/card.cpp b/kpat/card.cpp
new file mode 100644
index 00000000..983a901d
--- /dev/null
+++ b/kpat/card.cpp
@@ -0,0 +1,380 @@
+/******************************************************
+
+ Card.cpp -- support classes for patience type card games
+
+ 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.
+
+*******************************************************/
+
+#include <math.h>
+#include <assert.h>
+
+#include <qpainter.h>
+
+#include <kdebug.h>
+
+#include "card.h"
+#include "pile.h"
+#include "cardmaps.h"
+
+
+static const char *suit_names[] = {"Clubs", "Diamonds", "Hearts", "Spades"};
+static const char *rank_names[] = {"Ace", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight",
+ "Nine", "Ten", "Jack", "Queen", "King" };
+
+// Run time type id
+const int Card::RTTI = 1001;
+
+
+Card::Card( Rank r, Suit s, QCanvas* _parent )
+ : QCanvasRectangle( _parent ),
+ m_suit( s ), m_rank( r ),
+ m_source(0), scaleX(1.0), scaleY(1.0), tookDown(false)
+{
+ // Set the name of the card
+ // FIXME: i18n()
+ m_name = QString("%1 %2").arg(suit_names[s-1]).arg(rank_names[r-1]).utf8();
+
+ // Default for the card is face up, standard size.
+ m_faceup = true;
+ setSize( cardMap::CARDX(), cardMap::CARDY() );
+
+ m_destX = 0;
+ m_destY = 0;
+ m_destZ = 0;
+
+ m_flipping = false;
+ m_animSteps = 0;
+ m_flipSteps = 0;
+}
+
+
+Card::~Card()
+{
+ // If the card is in a pile, remove it from there.
+ if (source())
+ source()->remove(this);
+
+ hide();
+}
+
+
+// ----------------------------------------------------------------
+// Member functions regarding graphics
+
+
+// Return the pixmap of the card
+//
+QPixmap Card::pixmap() const
+{
+ return cardMap::self()->image( m_rank, m_suit );
+}
+
+
+// Turn the card if necessary. If the face gets turned up, the card
+// is activated at the same time.
+//
+void Card::turn( bool _faceup )
+{
+ if (m_faceup != _faceup) {
+ m_faceup = _faceup;
+ setActive(!isActive()); // abuse
+ }
+}
+
+// Draw the card on the painter 'p'.
+//
+void Card::draw( QPainter &p )
+{
+ QPixmap side;
+
+ // Get the image to draw (front / back)
+ if( isFaceUp() )
+ side = cardMap::self()->image( m_rank, m_suit, isSelected());
+ else
+ side = cardMap::self()->backSide();
+
+ // Rescale the image if necessary.
+ if (scaleX <= 0.98 || scaleY <= 0.98) {
+ QWMatrix s;
+ s.scale( scaleX, scaleY );
+ side = side.xForm( s );
+ int xoff = side.width() / 2;
+ int yoff = side.height() / 2;
+ p.drawPixmap( int(x() + cardMap::CARDX()/2 - xoff),
+ int(y() + cardMap::CARDY()/2 - yoff), side );
+ } else
+ p.drawPixmap( int(x()), int(y()), side );
+}
+
+
+void Card::moveBy(double dx, double dy)
+{
+ QCanvasRectangle::moveBy(dx, dy);
+}
+
+
+// Return the X of the cards real position. This is the destination
+// of the animation if animated, and the current X otherwise.
+//
+int Card::realX() const
+{
+ if (animated())
+ return m_destX;
+ else
+ return int(x());
+}
+
+
+// Return the Y of the cards real position. This is the destination
+// of the animation if animated, and the current Y otherwise.
+//
+int Card::realY() const
+{
+ if (animated())
+ return m_destY;
+ else
+ return int(y());
+}
+
+
+// Return the > of the cards real position. This is the destination
+// of the animation if animated, and the current Z otherwise.
+//
+int Card::realZ() const
+{
+ if (animated())
+ return m_destZ;
+ else
+ return int(z());
+}
+
+
+// Return the "face up" status of the card.
+//
+// This is the destination of the animation if animated and animation
+// is more than half way, the original if animated and animation is
+// less than half way, and the current "face up" status otherwise.
+//
+
+bool Card::realFace() const
+{
+ if (animated() && m_flipping) {
+ bool face = isFaceUp();
+ if ( m_animSteps >= m_flipSteps / 2 - 1 )
+ return !face;
+ else
+ return face;
+ } else
+ return isFaceUp();
+}
+
+
+/// the following copyright is for the flipping code
+/**********************************************************************
+** Copyright (C) 2000 Trolltech AS. All rights reserved.
+**
+** This file is part of Qt Palmtop Environment.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+
+// Used to create an illusion of the card being lifted while flipped.
+static const double flipLift = 1.2;
+
+// The current maximum Z value. This is used so that new cards always
+// get placed on top of the old ones and don't get placed in the
+// middle of a destination pile.
+int Card::Hz = 0;
+
+
+void Card::setZ(double z)
+{
+ QCanvasRectangle::setZ(z);
+ if (z > Hz)
+ Hz = int(z);
+}
+
+
+// Start a move of the card using animation.
+//
+// 'steps' is the number of steps the animation should take.
+//
+void Card::moveTo(int x2, int y2, int z2, int steps)
+{
+ m_destX = x2;
+ m_destY = y2;
+ m_destZ = z2;
+
+ double x1 = x();
+ double y1 = y();
+ double dx = x2 - x1;
+ double dy = y2 - y1;
+
+ if (!dx && !dy) {
+ setZ(z2);
+ return;
+ }
+ setZ(Hz++);
+
+ if (steps) {
+ // Ensure a good speed
+ while ( fabs(dx/steps)+fabs(dy/steps) < 5.0 && steps > 4 )
+ steps--;
+
+ setAnimated(true);
+ setVelocity(dx/steps, dy/steps);
+
+ m_animSteps = steps;
+
+ } else {
+ // _really_ fast
+ setAnimated(true);
+ setAnimated(false);
+ emit stoped(this);
+ }
+}
+
+
+// Animate a move to (x2, y2), and at the same time flip the card.
+//
+void Card::flipTo(int x2, int y2, int steps)
+{
+ // Check that we are not already animating.
+ assert(!animated());
+
+ int x1 = (int)x();
+ int y1 = (int)y();
+ double dx = x2 - x1;
+ double dy = y2 - y1;
+
+ // Mark this animation as a flip as well.
+ m_flipping = true;
+ m_flipSteps = steps;
+
+ // Set the target of the animation
+ m_destX = x2;
+ m_destY = y2;
+ m_destZ = int(z());
+
+ // Let the card be above all others during the animation.
+ setZ(Hz++);
+
+ m_animSteps = steps;
+ setVelocity(dx/m_animSteps, dy/m_animSteps-flipLift);
+
+ setAnimated(TRUE);
+}
+
+
+// Advance a card animation one step. This function adds flipping of
+// the card to the translation animation that QCanvasRectangle offers.
+//
+void Card::advance(int stage)
+{
+ if ( stage==1 ) {
+ // If the animation is finished, emit stoped. (FIXME: name)
+ if ( m_animSteps-- <= 0 ) {
+ setAnimated(false);
+ emit stoped(this);
+ } else {
+ // Animation is not finished. Check for flipping and add
+ // that animation to the simple translation.
+ if ( m_flipping ) {
+ if ( m_animSteps > m_flipSteps / 2 ) {
+ // animSteps = flipSteps .. flipSteps/2 (flip up) -> 1..0
+ scaleX = ((double)m_animSteps/m_flipSteps-0.5)*2;
+ } else {
+ // animSteps = flipSteps/2 .. 0 (flip down) -> 0..1
+ scaleX = 1-((double)m_animSteps/m_flipSteps)*2;
+ }
+ if ( m_animSteps == m_flipSteps / 2-1 ) {
+ setYVelocity(yVelocity()+flipLift*2);
+ turn( !isFaceUp() );
+ }
+ }
+ }
+ }
+
+ // Animate the translation of the card.
+ QCanvasRectangle::advance(stage);
+}
+
+
+// Set 'animated' status to a new value, and set secondary values as
+// well.
+//
+void Card::setAnimated(bool anim)
+{
+ // If no more animation, reset some other values as well.
+ if (animated() && !anim) {
+ // Reset all things that might have changed during the animation.
+ scaleX = 1.0;
+ scaleY = 1.0;
+ m_flipping = FALSE;
+ setVelocity(0, 0);
+
+ // Move the card to its destination immediately.
+ move(m_destX, m_destY);
+ setZ(m_destZ);
+ }
+
+ QCanvasRectangle::setAnimated(anim);
+}
+
+
+void Card::setTakenDown(bool td)
+{
+ if (td)
+ kdDebug(11111) << "took down " << name() << endl;
+ tookDown = td;
+}
+
+
+bool Card::takenDown() const
+{
+ return tookDown;
+}
+
+
+// Get the card to the top.
+
+void Card::getUp(int steps)
+{
+ m_destZ = int(z());
+ m_destX = int(x());
+ m_destY = int(y());
+ setZ(Hz+1);
+
+ // Animation
+ m_animSteps = steps;
+ setVelocity(0, 0);
+ setAnimated(TRUE);
+}
+
+#include "card.moc"
diff --git a/kpat/card.h b/kpat/card.h
new file mode 100644
index 00000000..61f07399
--- /dev/null
+++ b/kpat/card.h
@@ -0,0 +1,131 @@
+/*****************-*-C++-*-****************
+
+
+
+ Card.h -- movable and stackable cards
+ with check for legal moves
+
+
+
+ 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.
+
+ ****************************************************/
+
+
+#ifndef PATIENCE_CARD
+#define PATIENCE_CARD
+
+#include <qcanvas.h>
+
+// The following classes are defined in other headers:
+class cardPos;
+class Deck;
+class Dealer;
+class Pile;
+class Card;
+
+
+// A list of cards. Used in many places.
+typedef QValueList<Card*> CardList;
+
+
+// In kpat, a Card is an object that has at least two purposes:
+// - It has card properties (Suit, Rank, etc)
+// - It is a graphic entity on a QCanvas that can be moved around.
+//
+class Card: public QObject, public QCanvasRectangle {
+ Q_OBJECT
+
+public:
+ enum Suit { Clubs = 1, Diamonds, Hearts, Spades };
+ enum Rank { None = 0, Ace = 1, Two, Three, Four, Five, Six, Seven,
+ Eight, Nine, Ten, Jack, Queen, King };
+
+ Card( Rank r, Suit s, QCanvas *parent=0);
+ virtual ~Card();
+
+ // Properties of the card.
+ Suit suit() const { return m_suit; }
+ Rank rank() const { return m_rank; }
+ const QString name() const { return m_name; }
+
+ // Some basic tests.
+ bool isRed() const { return m_suit==Diamonds || m_suit==Hearts; }
+ bool isFaceUp() const { return m_faceup; }
+
+ QPixmap pixmap() const;
+
+ void turn(bool faceup = true);
+
+ static const int RTTI;
+
+ Pile *source() const { return m_source; }
+ void setSource(Pile *p) { m_source = p; }
+
+ virtual int rtti() const { return RTTI; }
+
+ virtual void moveBy(double dx, double dy);
+ void moveTo(int x2, int y2, int z, int steps);
+ void flipTo(int x, int y, int steps);
+ virtual void setAnimated(bool anim);
+ void setZ(double z);
+ void getUp(int steps = 12);
+
+ int realX() const;
+ int realY() const;
+ int realZ() const;
+ bool realFace() const;
+
+ void setTakenDown(bool td);
+ bool takenDown() const;
+
+signals:
+ void stoped(Card *c);
+
+protected:
+ void draw( QPainter &p ); // Redraw the card.
+ void advance(int stage);
+
+private:
+ // The card values.
+ Suit m_suit;
+ Rank m_rank;
+ QString m_name;
+
+ // Grapics properties.
+ bool m_faceup; // True if card lies with the face up.
+ Pile *m_source;
+
+ double scaleX;
+ double scaleY;
+
+ bool tookDown;
+
+ // Used for animation
+ int m_destX; // Destination point.
+ int m_destY;
+ int m_destZ;
+ int m_animSteps; // Let the animation take this many steps.
+
+ // Used if flipping during an animated move.
+ bool m_flipping;
+ int m_flipSteps;
+
+ // The maximum Z ever used.
+ static int Hz;
+};
+
+
+#endif
diff --git a/kpat/cardmaps.cpp b/kpat/cardmaps.cpp
new file mode 100644
index 00000000..5a2decae
--- /dev/null
+++ b/kpat/cardmaps.cpp
@@ -0,0 +1,248 @@
+/***********************-*-C++-*-********
+
+ cardmaps.cpp defines pixmaps for playing cards
+
+ 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.
+
+****************************************/
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include <qpainter.h>
+
+#include <kconfig.h>
+
+#include <config.h>
+#include "cardmaps.h"
+#include <stdlib.h>
+
+#include <config.h>
+#include <kdebug.h>
+#include <kapplication.h>
+#include <klocale.h>
+#include "version.h"
+#include <kstaticdeleter.h>
+#include <qimage.h>
+#include <kimageeffect.h>
+#include <kcarddialog.h>
+#include <kglobalsettings.h>
+#include <assert.h>
+
+cardMap *cardMap::_self = 0;
+static KStaticDeleter<cardMap> cms;
+
+cardMap::cardMap(const QColor &dim) : dimcolor(dim)
+{
+ assert(!_self);
+
+ card_width = 0;
+ card_height = 0;
+
+ kdDebug(11111) << "cardMap\n";
+ KConfig *config = kapp->config();
+ KConfigGroupSaver cs(config, settings_group );
+
+ QString bg = config->readEntry( "Back", KCardDialog::getDefaultDeck());
+ setBackSide( bg, false);
+
+ QString dir = config->readEntry("Cards", KCardDialog::getDefaultCardDir());
+ setCardDir( dir );
+
+ cms.setObject(_self, this);
+// kdDebug(11111) << "card " << CARDX << " " << CARDY << endl;
+}
+
+bool cardMap::setCardDir( const QString &dir)
+{
+ KConfig *config = kapp->config();
+ KConfigGroupSaver cs(config, settings_group );
+
+ // create an animation window while loading pixmaps (this
+ // may take a while (approx. 3 seconds on my AMD K6PR200)
+ bool animate = config->readBoolEntry( "Animation", true);
+
+ QWidget* w = 0;
+ QPainter p;
+ QTime t1, t2;
+
+ QString imgname = KCardDialog::getCardPath(dir, 11);
+
+ QImage image;
+ image.load(imgname);
+ if( image.isNull()) {
+ kdDebug(11111) << "cannot load card pixmap \"" << imgname << "\" in " << dir << "\n";
+ p.end();
+ delete w;
+ return false;
+ }
+
+ int old_card_width = card_width;
+ int old_card_height = card_height;
+
+ card_width = image.width();
+ card_height = image.height();
+
+ const int diff_x_between_cards = QMAX(card_width / 9, 1);
+ QString wait_message = i18n("please wait, loading cards...");
+ QString greeting = i18n("KPatience - a Solitaire game");
+
+ const int greeting_width = 20 + diff_x_between_cards * 52 + card_width;
+
+ if( animate ) {
+ t1 = QTime::currentTime();
+ w = new QWidget( 0, "", Qt::WStyle_Customize | Qt::WStyle_NoBorder | Qt::WStyle_Tool );
+ QRect dg = KGlobalSettings::splashScreenDesktopGeometry();
+ w->setBackgroundColor( Qt::darkGreen );
+ w->setGeometry( dg.left() + ( dg.width() - greeting_width ) / 2, dg.top() + ( dg.height() - 180 ) / 2, greeting_width, 180);
+ w->show();
+ qApp->processEvents();
+
+ p.begin( w );
+ p.drawText(0, 150, greeting_width, 20, Qt::AlignCenter,
+ wait_message );
+
+ p.setFont(QFont("Times", 24));
+ p.drawText(0, 0, greeting_width, 40, Qt::AlignCenter,
+ greeting);
+
+ p.setPen(QPen(QColor(0, 0, 0), 4));
+ p.setBrush(Qt::NoBrush);
+ p.drawRect(0, 0, greeting_width, 180);
+ p.flush();
+ }
+
+ setBackSide(back, true);
+
+ for(int idx = 1; idx < 53; idx++)
+ {
+ // translate index to suit/rank
+ // this is necessary since kpoker uses another
+ // mapping in the pictures
+ int rank = (idx - 1) / 4;
+ if(rank != 0)
+ rank = 13 - rank;
+ int suit = 0;
+ switch((idx - 1) % 4) {
+ case 0:
+ suit = 0;
+ break;
+ case 1:
+ suit = 3;
+ break;
+ case 2:
+ suit = 2;
+ break;
+ case 3:
+ suit = 1;
+ break;
+ }
+
+ imgname = KCardDialog::getCardPath(dir, idx);
+ image.load(imgname);
+
+ if( image.isNull() || image.width() != card_width || image.height() != card_height ) {
+ kdDebug(11111) << "cannot load card pixmap \"" << imgname << "\" in (" << idx << ") " << dir << "\n";
+ p.end();
+ delete w;
+ card_width = old_card_width;
+ card_height = old_card_height;
+
+ setBackSide(back, true);
+ return false;
+ }
+
+ img[rank][suit].normal.convertFromImage(image);
+ KImageEffect::fade(image, 0.4, dimcolor);
+ img[rank][suit].inverted.convertFromImage(image);
+
+ if( animate )
+ {
+ if( idx > 1 )
+ p.drawPixmap( 10 + ( idx - 1 ) * diff_x_between_cards, 45, back );
+ p.drawPixmap( 10 + idx * diff_x_between_cards, 45, img[ rank ][ suit ].normal );
+ p.flush();
+ }
+ }
+
+ if( animate )
+ {
+ const int time_to_see = 900;
+ p.end();
+ t2 = QTime::currentTime();
+ if(t1.msecsTo(t2) < time_to_see)
+ usleep((time_to_see-t1.msecsTo(t2))*1000);
+ delete w;
+ }
+
+ return true;
+}
+
+bool cardMap::setBackSide( const QPixmap &pm, bool scale )
+{
+ if (pm.isNull())
+ return false;
+
+ back = pm;
+
+ if(scale && (back.width() != card_width ||
+ back.height() != card_height))
+ {
+ kdDebug(11111) << "scaling back!!\n";
+ // scale to fit size
+ QWMatrix wm;
+ wm.scale(((float)(card_width))/back.width(),
+ ((float)(card_height))/back.height());
+ back = back.xForm(wm);
+ }
+
+ return true;
+}
+
+int cardMap::CARDX() {
+ return self()->card_width; // 72;
+}
+
+int cardMap::CARDY() {
+ return self()->card_height; // 96;
+}
+
+QPixmap cardMap::backSide() const
+{
+ return back;
+}
+
+QPixmap cardMap::image( Card::Rank _rank, Card::Suit _suit, bool inverted) const
+{
+ if( 1 <= _rank && _rank <= 13
+ && 1 <= _suit && _suit <= 4 )
+ {
+ if (inverted)
+ return img[ _rank - 1 ][ _suit - 1 ].inverted;
+ else
+ return img[ _rank - 1 ][ _suit - 1 ].normal;
+ }
+ else
+ {
+ kdError() << "access to invalid card " << int(_rank) << ", " << int(_suit) << endl;
+ }
+ return 0;
+}
+
+cardMap *cardMap::self() {
+ assert(_self);
+ return _self;
+}
+
diff --git a/kpat/cardmaps.h b/kpat/cardmaps.h
new file mode 100644
index 00000000..8bc8d92c
--- /dev/null
+++ b/kpat/cardmaps.h
@@ -0,0 +1,59 @@
+/***********************-*-C++-*-********
+
+ cardmaps.h defines pixmaps for playing cards
+
+ 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.
+
+****************************************/
+
+#ifndef P_HACK_CARDMAP
+#define P_HACK_CARDMAP
+
+#include "card.h"
+
+class cardMap
+{
+public:
+
+ static cardMap *self();
+ cardMap(const QColor &dimcolor);
+
+ static int CARDX();
+ static int CARDY();
+
+ static const int NumColors = 4;
+ static const int CardsPerColor = 13;
+
+ QPixmap image( Card::Rank _rank, Card::Suit _suit, bool inverted = false) const;
+ QPixmap backSide() const;
+ bool setCardDir( const QString &dir);
+ bool setBackSide( const QPixmap & _pix, bool scale = true);
+
+private:
+
+ cardMap();
+ struct
+ {
+ QPixmap normal;
+ QPixmap inverted;
+ } img[ CardsPerColor ][ NumColors ];
+ QPixmap back;
+ QColor dimcolor;
+ int card_width, card_height;
+
+ static cardMap *_self;
+};
+
+#endif
diff --git a/kpat/clock.cpp b/kpat/clock.cpp
new file mode 100644
index 00000000..7fcb94b4
--- /dev/null
+++ b/kpat/clock.cpp
@@ -0,0 +1,91 @@
+#include "clock.h"
+#include <klocale.h>
+#include "deck.h"
+#include <assert.h>
+#include "cardmaps.h"
+
+Clock::Clock( KMainWindow* 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;
+
+ deck = Deck::new_deck(this);
+ deck->move(10, 10+dist_y*3);
+ deck->hide();
+
+ for (int i=0; i<12; i++) {
+ target[i] = new Pile(i+1, this);
+ const double ys[12] = { 0./96, 15./96, 52./96, 158./96, 264./96, 301./96, 316./96, 301./96, 264./96, 158./96, 52./96, 15./96};
+ const double xs[12] = { 200./72, 280./72, 360./72, 400./72, 360./72, 280./72, 200./72, 120./72, 40./72, 0./72, 40./72, 120./72};
+ target[i]->move(15 + cardMap::CARDX() * 24 / 5 + xs[i] * cardMap::CARDX(), 10 + ys[i] * cardMap::CARDY());
+ target[i]->setCheckIndex(1);
+ target[i]->setTarget(true);
+ target[i]->setRemoveFlags(Pile::disallow);
+ }
+
+ for (int i=0; i<8; i++) {
+ store[i] = new Pile(14+i, this);
+ store[i]->move(15+dist_x*(i%4), 10 + cardMap::CARDY() * 5 / 2 * (i/4));
+ store[i]->setAddFlags(Pile::addSpread);
+ store[i]->setCheckIndex(0);
+ }
+
+ setActions(Dealer::Hint | Dealer::Demo);
+}
+
+void Clock::restart()
+{
+ deck->collectAndShuffle();
+ deal();
+}
+
+bool Clock::checkAdd( int ci, const Pile *c1, const CardList& c2) const
+{
+ Card *newone = c2.first();
+ if (ci == 0) {
+ if (c1->isEmpty())
+ return true;
+
+ return (newone->rank() == c1->top()->rank() - 1);
+ } else {
+ if (c1->top()->suit() != newone->suit())
+ return false;
+ if (c1->top()->rank() == Card::King)
+ return (newone->rank() == Card::Ace);
+ return (newone->rank() == c1->top()->rank() + 1);
+ }
+}
+
+void Clock::deal() {
+ static const Card::Suit suits[12] = { Card::Diamonds, Card::Spades, Card::Hearts, Card::Clubs,
+ Card::Diamonds, Card::Spades, Card::Hearts, Card::Clubs,
+ Card::Diamonds, Card::Spades, Card::Hearts, Card::Clubs, };
+ static const Card::Rank ranks[12] = { Card::Nine, Card::Ten, Card::Jack, Card::Queen,
+ Card::King, Card::Two, Card::Three, Card::Four,
+ Card::Five, Card::Six, Card::Seven, Card::Eight};
+
+ int j = 0;
+ while (!deck->isEmpty()) {
+ Card *c = deck->nextCard();
+ for (int i = 0; i < 12; i++)
+ if (c->rank() == ranks[i] && c->suit() == suits[i]) {
+ target[i]->add(c, false, true);
+ c = 0;
+ break;
+ }
+ if (c)
+ store[j++]->add(c, false, true);
+ if (j == 8)
+ j = 0;
+ }
+}
+
+static class LocalDealerInfo11 : public DealerInfo
+{
+public:
+ LocalDealerInfo11() : DealerInfo(I18N_NOOP("G&randfather's Clock"), 11) {}
+ virtual Dealer *createGame(KMainWindow *parent) { return new Clock(parent); }
+} gfi11;
+
+#include "clock.moc"
diff --git a/kpat/clock.h b/kpat/clock.h
new file mode 100644
index 00000000..42521862
--- /dev/null
+++ b/kpat/clock.h
@@ -0,0 +1,24 @@
+#ifndef CLOCK_H
+#define CLOCK_H
+
+#include "dealer.h"
+
+class Clock : public Dealer {
+ Q_OBJECT
+
+public:
+ Clock( KMainWindow* parent=0, const char* name=0);
+ virtual bool checkAdd ( int checkIndex, const Pile *c1, const CardList& c2) const;
+ virtual bool startAutoDrop() { return false; }
+
+public slots:
+ void deal();
+ virtual void restart();
+
+private:
+ Pile* store[8];
+ Pile* target[12];
+ Deck *deck;
+};
+
+#endif
diff --git a/kpat/computation.cpp b/kpat/computation.cpp
new file mode 100644
index 00000000..22f104aa
--- /dev/null
+++ b/kpat/computation.cpp
@@ -0,0 +1,120 @@
+/***********************-*-C++-*-********
+
+ computation.h 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.
+
+//
+// This one was discussed on the newsgroup rec.games.abstract
+//
+****************************************/
+
+#include "computation.h"
+#include <klocale.h>
+#include "deck.h"
+#include <assert.h>
+#include "cardmaps.h"
+
+Computation::Computation( KMainWindow *parent, const char *name )
+ :Dealer( parent, name)
+{
+ deck = Deck::new_deck(this);
+ deck->hide();
+
+ for (int i = 0; i < 4; i++) {
+ play[i] = new Pile(1 + i, this);
+ play[i]->move(10 + (i+1) * cardMap::CARDX() * 14 / 10, 10 + cardMap::CARDY() * 15 / 10);
+ play[i]->setAddFlags(Pile::addSpread);
+ play[i]->setCheckIndex(1);
+
+ target[i] = new Pile(5 + i, this);
+ target[i]->move(10 + (i+1) * cardMap::CARDX() * 14 / 10, 10);
+ target[i]->setRemoveFlags(Pile::disallow);
+ target[i]->setCheckIndex(0);
+ target[i]->setTarget(true);
+ }
+
+ pile = new Pile(13, this);
+ pile->setAddFlags(Pile::disallow);
+ pile->setRemoveFlags(Pile::autoTurnTop);
+ pile->move(10, 10);
+
+ setActions(Dealer::Demo | Dealer::Hint);
+}
+
+void Computation::restart() {
+ deck->collectAndShuffle();
+ deal();
+}
+
+void Computation::deal() {
+ while (!deck->isEmpty()) {
+ Card *c = deck->nextCard();
+ pile->add(c, true, false);
+ }
+ // no animation
+ pile->top()->turn(true);
+}
+
+inline bool matches(const CardList &cl, Card *start, int offset)
+{
+ Card *before = start; // maybe 0 for ignore first card
+ for (CardList::ConstIterator it = cl.begin(); it != cl.end(); ++it)
+ {
+ if (before && (*it)->rank() % 13 != (before->rank() + offset) % 13)
+ return false;
+ before = *it;
+ }
+ return true;
+}
+
+bool Computation::checkStore( const Pile*, const CardList& cl) const
+{
+ if (cl.count() != 1)
+ return false;
+ return (cl.first()->source()->index() == 13);
+}
+
+bool Computation::checkAdd( int index, const Pile* c1, const CardList& cl) const
+{
+ if (index == 1)
+ return checkStore(c1, cl);
+
+ assert(c1->index() >= 5 && c1->index() <= 8);
+
+ if ( c1->top() && c1->top()->rank() == Card::King) // finished
+ return false;
+
+ if ( c1->cardsLeft() == 13 )
+ return false;
+
+ int offset = c1->index() - 4;
+
+ if (c1->isEmpty()) {
+ Card::Rank start = static_cast<Card::Rank>(Card::Ace + (offset - 1));
+ return cl.first()->rank() == start && matches(cl, 0, offset);
+ }
+
+ return matches(cl, c1->top(), offset);
+}
+
+static class LocalDealerInfo6 : public DealerInfo
+{
+public:
+ LocalDealerInfo6() : DealerInfo(I18N_NOOP("&Calculation"), 6) {}
+ virtual Dealer *createGame(KMainWindow *parent) { return new Computation(parent); }
+} ldi6;
+
+#include "computation.moc"
diff --git a/kpat/computation.h b/kpat/computation.h
new file mode 100644
index 00000000..3b9b3fbc
--- /dev/null
+++ b/kpat/computation.h
@@ -0,0 +1,53 @@
+/***********************-*-C++-*-********
+
+ computation.h 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.
+
+//
+// This one was discussed on the newsgroup rec.games.abstract
+//
+
+
+****************************************/
+
+#ifndef P_COMPUTATION_H
+#define P_COMPUTATION_H
+
+#include "dealer.h"
+
+class Computation : public Dealer {
+ Q_OBJECT
+
+public:
+ Computation( KMainWindow *parent = 0, const char *name=0 );
+
+ virtual void restart();
+
+private:
+ Card *getCardByValue( char v );
+ void deal();
+
+ bool checkStore(const Pile* c1, const CardList& c2) const;
+ virtual bool checkAdd( int index, const Pile* c1, const CardList& c2) const;
+
+ Deck *deck;
+ Pile *pile;
+
+ Pile *play[4];
+ Pile *target[4];
+};
+
+#endif
diff --git a/kpat/copyright.h b/kpat/copyright.h
new file mode 100644
index 00000000..5e1f2b5b
--- /dev/null
+++ b/kpat/copyright.h
@@ -0,0 +1,154 @@
+/*
+
+
+ Patience -- a general class for patience card games
+ 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.
+
+ except for the card bitmaps contained in the files
+
+ newface.bm rank.bm suit.bm
+
+which are
+
+
+ (c) Copyright 1989, Donald R. Woods and Sun Microsystems, Inc.
+ (c) Copyright 1990, David Lemke and Network Computing Devices Inc.
+
+
+
+ See the statement below for the terms of the copyright.
+
+
+ */
+
+
+
+
+
+
+/**** Copyright statement from spider/copyright.h follows this line ****/
+
+
+
+
+
+/*
+ * Copyright 1990 Heather Rose and Sun Microsystems, Inc.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the names of Donald Woods and Sun Microsystems not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission. Heather Rose and Sun Microsystems not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission. Heather Rose and Sun Microsystems make
+ * no representations about the suitability of this software for any purpose.
+ * It is provided "as is" without express or implied warranty.
+ *
+ * THE ABOVE-NAMED DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT
+ * SHALL HEATHER ROSE OR SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ *
+ * Author:
+ * Heather Rose
+ * hrose@sun.com
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, CA 94043
+ */
+
+/*
+ * Copyright 1990 David Lemke and Network Computing Devices
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Network Computing Devices not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. Network Computing
+ * Devices makes no representations about the suitability of this software
+ * for any purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * NETWORK COMPUTING DEVICES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL NETWORK COMPUTING DEVICES BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
+ * OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author:
+ * Dave Lemke
+ * lemke@ncd.com
+ *
+ * Network Computing Devices, Inc
+ * 350 North Bernardo Ave
+ * Mountain View, CA 94043
+ *
+ * @(#)copyright.h 2.2 90/04/27
+ *
+ */
+
+/*
+% Copyright (c) 1989, Donald R. Woods and Sun Microsystems, Inc.
+%
+% Permission to use, copy, modify, distribute, and sell this software and its
+% documentation for any purpose is hereby granted without fee, provided that
+% the above copyright notice appear in all copies and that both that copyright
+% notice and this permission notice appear in supporting documentation, and
+% that the names of Donald Woods and Sun Microsystems not be used in
+% advertising or publicity pertaining to distribution of the software without
+% specific, written prior permission. Donald Woods and Sun Microsystems make
+% no representations about the suitability of this software for any purpose.
+% It is provided "as is" without express or implied warranty.
+%
+% THE ABOVE-NAMED DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+% INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT
+% SHALL DONALD WOODS OR SUN MICROSYSTEMS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+% CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+% DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+% TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+% OF THIS SOFTWARE.
+%
+% History: Spider is a solitaire card game that can be found in various books
+% of same; the rules are presumed to be in the public domain. The author's
+% first computer implementation was on the Stanford Artificial Intelligence Lab
+% system (SAIL). It was later ported to the Xerox Development Environment.
+% The card images are loosely based on scanned-in images but were largely
+% redrawn by the author with help from Larry Rosenberg.
+%
+% This program is written entirely in NeWS and runs on OPEN WINDOWS 1.0.
+% It could be made to run much faster if parts of it were written in C, using
+% NeWS mainly for its display and input capabilities, but that is left as an
+% exercise for the reader. Spider may also run with little or no modification
+% on subsequent releases of OPEN WINDOWS, but no guarantee is made on this
+% point (nor any other; see above!). To run Spider, feed this file to 'psh'.
+%
+% Author: Don Woods
+% woods@sun.com
+%
+% Sun Microsystems, Inc.
+% 2550 Garcia Avenue
+% Mountain View, CA 94043
+*/
diff --git a/kpat/dealer.cpp b/kpat/dealer.cpp
new file mode 100644
index 00000000..2100ae2e
--- /dev/null
+++ b/kpat/dealer.cpp
@@ -0,0 +1,1455 @@
+#include "dealer.h"
+#include <kstaticdeleter.h>
+#include <kdebug.h>
+#include "deck.h"
+#include <assert.h>
+#include "kmainwindow.h"
+#include <kapplication.h>
+#include <kpixmapeffect.h>
+#include <qtimer.h>
+#include <kaction.h>
+#include <klocale.h>
+#include "cardmaps.h"
+#include "speeds.h"
+#include <kconfig.h>
+#include "version.h"
+
+
+// ================================================================
+// class MoveHint
+
+
+MoveHint::MoveHint(Card *card, Pile *to, bool d)
+{
+ m_card = card;
+ m_to = to;
+ m_dropiftarget = d;
+}
+
+
+// ================================================================
+// class DealerInfoList
+
+
+DealerInfoList *DealerInfoList::_self = 0;
+static KStaticDeleter<DealerInfoList> dl;
+
+
+DealerInfoList *DealerInfoList::self()
+{
+ if (!_self)
+ _self = dl.setObject(_self, new DealerInfoList());
+ return _self;
+}
+
+void DealerInfoList::add(DealerInfo *dealer)
+{
+ list.append(dealer);
+}
+
+
+// ================================================================
+// class Dealer
+
+
+Dealer *Dealer::s_instance = 0;
+
+
+Dealer::Dealer( KMainWindow* _parent , const char* _name )
+ : QCanvasView( 0, _parent, _name ),
+ towait(0),
+ myActions(0),
+ ademo(0),
+ ahint(0),
+ aredeal(0),
+ takeTargets(false),
+ _won(false),
+ _waiting(0),
+ stop_demo_next(false),
+ _autodrop(true),
+ _gameRecorded(false)
+{
+ setResizePolicy(QScrollView::Manual);
+ setVScrollBarMode(AlwaysOff);
+ setHScrollBarMode(AlwaysOff);
+
+ setGameNumber(kapp->random());
+ myCanvas.setAdvancePeriod(30);
+ // myCanvas.setBackgroundColor( darkGreen );
+ setCanvas(&myCanvas);
+ myCanvas.setDoubleBuffering(true);
+
+ undoList.setAutoDelete(true);
+
+ demotimer = new QTimer(this);
+
+ connect(demotimer, SIGNAL(timeout()), SLOT(demo()));
+
+ assert(!s_instance);
+ s_instance = this;
+}
+
+
+const Dealer *Dealer::instance()
+{
+ return s_instance;
+}
+
+
+void Dealer::setBackgroundPixmap(const QPixmap &background, const QColor &midcolor)
+{
+ _midcolor = midcolor;
+ canvas()->setBackgroundPixmap(background);
+ for (PileList::Iterator it = piles.begin(); it != piles.end(); ++it) {
+ (*it)->resetCache();
+ (*it)->initSizes();
+ }
+}
+
+void Dealer::setupActions() {
+
+ QPtrList<KAction> actionlist;
+
+ kdDebug(11111) << "setupActions " << actions() << endl;
+
+ if (actions() & Dealer::Hint) {
+
+ ahint = new KAction( i18n("&Hint"), QString::fromLatin1("wizard"), Key_H, this,
+ SLOT(hint()),
+ parent()->actionCollection(), "game_hint");
+ actionlist.append(ahint);
+ } else
+ ahint = 0;
+
+ if (actions() & Dealer::Demo) {
+ ademo = new KToggleAction( i18n("&Demo"), QString::fromLatin1("1rightarrow"), CTRL+Key_D, this,
+ SLOT(toggleDemo()),
+ parent()->actionCollection(), "game_demo");
+ actionlist.append(ademo);
+ } else
+ ademo = 0;
+
+ if (actions() & Dealer::Redeal) {
+ aredeal = new KAction (i18n("&Redeal"), QString::fromLatin1("queue"), 0, this,
+ SLOT(redeal()),
+ parent()->actionCollection(), "game_redeal");
+ actionlist.append(aredeal);
+ } else
+ aredeal = 0;
+
+ parent()->guiFactory()->plugActionList( parent(), QString::fromLatin1("game_actions"), actionlist);
+}
+
+Dealer::~Dealer()
+{
+ if (!_won)
+ countLoss();
+ clearHints();
+ parent()->guiFactory()->unplugActionList( parent(), QString::fromLatin1("game_actions"));
+
+ while (!piles.isEmpty())
+ delete piles.first(); // removes itself
+
+ if (s_instance == this)
+ s_instance = 0;
+}
+
+KMainWindow *Dealer::parent() const
+{
+ return dynamic_cast<KMainWindow*>(QCanvasView::parent());
+}
+
+
+// ----------------------------------------------------------------
+
+
+void Dealer::hint()
+{
+ unmarkAll();
+ clearHints();
+ getHints();
+ for (HintList::ConstIterator it = hints.begin(); it != hints.end(); ++it)
+ mark((*it)->card());
+ clearHints();
+ canvas()->update();
+}
+
+
+void Dealer::getHints()
+{
+ for (PileList::Iterator it = piles.begin(); it != piles.end(); ++it)
+ {
+ if (!takeTargetForHints() && (*it)->target())
+ continue;
+
+ Pile *store = *it;
+ if (store->isEmpty())
+ continue;
+// kdDebug(11111) << "trying " << store->top()->name() << endl;
+
+ CardList cards = store->cards();
+ while (cards.count() && !cards.first()->realFace()) cards.remove(cards.begin());
+
+ CardList::Iterator iti = cards.begin();
+ while (iti != cards.end())
+ {
+ if (store->legalRemove(*iti)) {
+// kdDebug(11111) << "could remove " << (*iti)->name() << endl;
+ for (PileList::Iterator pit = piles.begin(); pit != piles.end(); ++pit)
+ {
+ Pile *dest = *pit;
+ if (dest == store)
+ continue;
+ if (store->indexOf(*iti) == 0 && dest->isEmpty() && !dest->target())
+ continue;
+ if (!dest->legalAdd(cards))
+ continue;
+
+ bool old_prefer = checkPrefering( dest->checkIndex(), dest, cards );
+ if (!takeTargetForHints() && dest->target())
+ newHint(new MoveHint(*iti, dest));
+ else {
+ store->hideCards(cards);
+ // if it could be here as well, then it's no use
+ if ((store->isEmpty() && !dest->isEmpty()) || !store->legalAdd(cards))
+ newHint(new MoveHint(*iti, dest));
+ else {
+ if (old_prefer && !checkPrefering( store->checkIndex(),
+ store, cards ))
+ { // if checkPrefers says so, we add it nonetheless
+ newHint(new MoveHint(*iti, dest));
+ }
+ }
+ store->unhideCards(cards);
+ }
+ }
+ }
+ cards.remove(iti);
+ iti = cards.begin();
+ }
+ }
+}
+
+bool Dealer::checkPrefering( int /*checkIndex*/, const Pile *, const CardList& ) const
+{
+ return false;
+}
+
+void Dealer::clearHints()
+{
+ for (HintList::Iterator it = hints.begin(); it != hints.end(); ++it)
+ delete *it;
+ hints.clear();
+}
+
+void Dealer::newHint(MoveHint *mh)
+{
+ hints.append(mh);
+}
+
+bool Dealer::isMoving(Card *c) const
+{
+ return movingCards.find(c) != movingCards.end();
+}
+
+void Dealer::contentsMouseMoveEvent(QMouseEvent* e)
+{
+ if (movingCards.isEmpty())
+ return;
+
+ moved = true;
+
+ for (CardList::Iterator it = movingCards.begin(); it != movingCards.end(); ++it)
+ {
+ (*it)->moveBy(e->pos().x() - moving_start.x(),
+ e->pos().y() - moving_start.y());
+ }
+
+ PileList sources;
+ QCanvasItemList list = canvas()->collisions(movingCards.first()->rect());
+
+ for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it)
+ {
+ if ((*it)->rtti() == Card::RTTI) {
+ Card *c = dynamic_cast<Card*>(*it);
+ assert(c);
+ if (!c->isFaceUp())
+ continue;
+ if (c->source() == movingCards.first()->source())
+ continue;
+ if (sources.findIndex(c->source()) != -1)
+ continue;
+ sources.append(c->source());
+ } else {
+ if ((*it)->rtti() == Pile::RTTI) {
+ Pile *p = static_cast<Pile*>(*it);
+ if (p->isEmpty() && !sources.contains(p))
+ sources.append(p);
+ } else {
+ kdDebug(11111) << "unknown object " << *it << " " << (*it)->rtti() << endl;
+ }
+ }
+ }
+
+ // TODO some caching of the results
+ unmarkAll();
+
+ for (PileList::Iterator it = sources.begin(); it != sources.end(); ++it)
+ {
+ bool b = (*it)->legalAdd(movingCards);
+ if (b) {
+ if ((*it)->isEmpty()) {
+ (*it)->setSelected(true);
+ marked.append(*it);
+ } else {
+ mark((*it)->top());
+ }
+ }
+ }
+
+ moving_start = e->pos();
+ canvas()->update();
+}
+
+void Dealer::mark(Card *c)
+{
+ c->setSelected(true);
+ if (!marked.contains(c))
+ marked.append(c);
+}
+
+void Dealer::unmarkAll()
+{
+ for (QCanvasItemList::Iterator it = marked.begin(); it != marked.end(); ++it)
+ {
+ (*it)->setSelected(false);
+ }
+ marked.clear();
+}
+
+void Dealer::contentsMousePressEvent(QMouseEvent* e)
+{
+ unmarkAll();
+ stopDemo();
+ if (waiting())
+ return;
+
+ QCanvasItemList list = canvas()->collisions(e->pos());
+
+ kdDebug(11111) << "mouse pressed " << list.count() << " " << canvas()->allItems().count() << endl;
+ moved = false;
+
+ if (!list.count())
+ return;
+
+ if (e->button() == LeftButton) {
+ if (list.first()->rtti() == Card::RTTI) {
+ Card *c = dynamic_cast<Card*>(list.first());
+ assert(c);
+ CardList mycards = c->source()->cardPressed(c);
+ for (CardList::Iterator it = mycards.begin(); it != mycards.end(); ++it)
+ (*it)->setAnimated(false);
+ movingCards = mycards;
+ moving_start = e->pos();
+ }
+ return;
+ }
+
+ if (e->button() == RightButton) {
+ if (list.first()->rtti() == Card::RTTI) {
+ Card *preview = dynamic_cast<Card*>(list.first());
+ assert(preview);
+ if (!preview->animated() && !isMoving(preview))
+ preview->getUp();
+ }
+ return;
+ }
+
+ // if it's nothing else, we move the cards back
+ contentsMouseReleaseEvent(e);
+
+}
+
+class Hit {
+public:
+ Pile *source;
+ QRect intersect;
+ bool top;
+};
+typedef QValueList<Hit> HitList;
+
+void Dealer::contentsMouseReleaseEvent( QMouseEvent *e)
+{
+ if (!moved) {
+ if (!movingCards.isEmpty()) {
+ movingCards.first()->source()->moveCardsBack(movingCards);
+ movingCards.clear();
+ }
+ QCanvasItemList list = canvas()->collisions(e->pos());
+ if (list.isEmpty())
+ return;
+ QCanvasItemList::Iterator it = list.begin();
+ if ((*it)->rtti() == Card::RTTI) {
+ Card *c = dynamic_cast<Card*>(*it);
+ assert(c);
+ if (!c->animated()) {
+ if ( cardClicked(c) ) {
+ countGame();
+ }
+ takeState();
+ canvas()->update();
+ }
+ return;
+ }
+ if ((*it)->rtti() == Pile::RTTI) {
+ Pile *c = dynamic_cast<Pile*>(*it);
+ assert(c);
+ pileClicked(c);
+ takeState();
+ canvas()->update();
+ return;
+ }
+ }
+
+ if (!movingCards.count())
+ return;
+ Card *c = static_cast<Card*>(movingCards.first());
+ assert(c);
+
+ unmarkAll();
+
+ QCanvasItemList list = canvas()->collisions(movingCards.first()->rect());
+ HitList sources;
+
+ for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it)
+ {
+ if ((*it)->rtti() == Card::RTTI) {
+ Card *c = dynamic_cast<Card*>(*it);
+ assert(c);
+ if (!c->isFaceUp())
+ continue;
+ if (c->source() == movingCards.first()->source())
+ continue;
+ Hit t;
+ t.source = c->source();
+ t.intersect = c->rect().intersect(movingCards.first()->rect());
+ t.top = (c == c->source()->top());
+
+ bool found = false;
+ for (HitList::Iterator hi = sources.begin(); hi != sources.end(); ++hi)
+ {
+ if ((*hi).source == c->source()) {
+ found = true;
+ if ((*hi).intersect.width() * (*hi).intersect.height() >
+ t.intersect.width() * t.intersect.height())
+ {
+ (*hi).intersect = t.intersect;
+ (*hi).top |= t.top;
+ }
+ }
+ }
+ if (found)
+ continue;
+
+ sources.append(t);
+ } else {
+ if ((*it)->rtti() == Pile::RTTI) {
+ Pile *p = static_cast<Pile*>(*it);
+ if (p->isEmpty())
+ {
+ Hit t;
+ t.source = p;
+ t.intersect = p->rect().intersect(movingCards.first()->rect());
+ t.top = true;
+ sources.append(t);
+ }
+ } else {
+ kdDebug(11111) << "unknown object " << *it << " " << (*it)->rtti() << endl;
+ }
+ }
+ }
+
+ for (HitList::Iterator it = sources.begin(); it != sources.end(); )
+ {
+ if (!(*it).source->legalAdd(movingCards))
+ it = sources.remove(it);
+ else
+ ++it;
+ }
+
+ if (sources.isEmpty()) {
+ c->source()->moveCardsBack(movingCards);
+ } else {
+ HitList::Iterator best = sources.begin();
+ HitList::Iterator it = best;
+ for (++it; it != sources.end(); ++it )
+ {
+ if ((*it).intersect.width() * (*it).intersect.height() >
+ (*best).intersect.width() * (*best).intersect.height()
+ || ((*it).top && !(*best).top))
+ {
+ best = it;
+ }
+ }
+ countGame();
+ c->source()->moveCards(movingCards, (*best).source);
+ takeState();
+ }
+ movingCards.clear();
+ canvas()->update();
+}
+
+void Dealer::contentsMouseDoubleClickEvent( QMouseEvent*e )
+{
+ stopDemo();
+ unmarkAll();
+ if (waiting())
+ return;
+
+ if (!movingCards.isEmpty()) {
+ movingCards.first()->source()->moveCardsBack(movingCards);
+ movingCards.clear();
+ }
+ QCanvasItemList list = canvas()->collisions(e->pos());
+ if (list.isEmpty())
+ return;
+ QCanvasItemList::Iterator it = list.begin();
+ if ((*it)->rtti() != Card::RTTI)
+ return;
+ Card *c = dynamic_cast<Card*>(*it);
+ assert(c);
+ if (!c->animated()) {
+ if ( cardDblClicked(c) ) {
+ countGame();
+ }
+ takeState();
+ }
+}
+
+QSize Dealer::minimumCardSize() const
+{
+ return minsize;
+}
+
+void Dealer::resizeEvent(QResizeEvent *e)
+{
+ int x = width();
+ int y = height();
+ int hs = horizontalScrollBar()->sizeHint().height();
+ int vs = verticalScrollBar()->sizeHint().width();
+
+ int mx = minsize.width();
+ int my = minsize.height();
+
+ int dx = x;
+ int dy = y;
+ bool showh = false;
+ bool showv = false;
+
+ if (mx > x) {
+ dx = mx;
+ if (my + vs < y)
+ dy -= vs;
+ else {
+ showv = true;
+ }
+ showh = true;
+ } else if (my > y) {
+ dy = my;
+ if (mx + hs < x)
+ dx -= hs;
+ else
+ showh = true;
+ showv = true;
+ }
+ canvas()->resize(dx, dy);
+ resizeContents(dx, dy);
+ setVScrollBarMode(showv ? AlwaysOn : AlwaysOff);
+ setHScrollBarMode(showh ? AlwaysOn : AlwaysOff);
+
+ if (!e)
+ updateScrollBars();
+ else
+ QCanvasView::resizeEvent(e);
+}
+
+bool Dealer::cardClicked(Card *c) {
+ return c->source()->cardClicked(c);
+}
+
+void Dealer::pileClicked(Pile *c) {
+ c->cardClicked(0);
+}
+
+bool Dealer::cardDblClicked(Card *c)
+{
+ if (c->source()->cardDblClicked(c))
+ return true;
+
+ if (c->animated())
+ return false;
+
+ if (c == c->source()->top() && c->realFace()) {
+ Pile *tgt = findTarget(c);
+ if (tgt) {
+ CardList empty;
+ empty.append(c);
+ c->source()->moveCards(empty, tgt);
+ canvas()->update();
+ return true;
+ }
+ }
+ return false;
+}
+
+void Dealer::startNew()
+{
+ if (!_won)
+ countLoss();
+ if ( ahint )
+ ahint->setEnabled( true );
+ if ( ademo )
+ ademo->setEnabled( true );
+ if ( aredeal )
+ aredeal->setEnabled( true );
+ toldAboutLostGame = false;
+ minsize = QSize(0,0);
+ _won = false;
+ _waiting = 0;
+ _gameRecorded=false;
+ kdDebug(11111) << "startNew stopDemo\n";
+ stopDemo();
+ kdDebug(11111) << "startNew unmarkAll\n";
+ unmarkAll();
+ kdDebug(11111) << "startNew setAnimated(false)\n";
+ QCanvasItemList list = canvas()->allItems();
+ for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) {
+ if ((*it)->rtti() == Card::RTTI)
+ static_cast<Card*>(*it)->disconnect();
+
+ (*it)->setAnimated(true);
+ (*it)->setAnimated(false);
+ }
+
+ undoList.clear();
+ emit undoPossible(false);
+ emit updateMoves();
+ kdDebug(11111) << "startNew restart\n";
+ restart();
+ takeState();
+ Card *towait = 0;
+ for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it) {
+ if ((*it)->rtti() == Card::RTTI) {
+ towait = static_cast<Card*>(*it);
+ if (towait->animated())
+ break;
+ }
+ }
+
+ kdDebug(11111) << "startNew takeState\n";
+ if (!towait)
+ takeState();
+ else
+ connect(towait, SIGNAL(stoped(Card*)), SLOT(slotTakeState(Card *)));
+ resizeEvent(0);
+}
+
+void Dealer::slotTakeState(Card *c) {
+ if (c)
+ c->disconnect();
+ takeState();
+}
+
+void Dealer::enlargeCanvas(QCanvasRectangle *c)
+{
+ if (!c->isVisible() || c->animated())
+ return;
+
+ bool changed = false;
+
+ if (c->x() + c->width() + 10 > minsize.width()) {
+ minsize.setWidth(int(c->x()) + c->width() + 10);
+ changed = true;
+ }
+ if (c->y() + c->height() + 10 > minsize.height()) {
+ minsize.setHeight(int(c->y()) + c->height() + 10);
+ changed = true;
+ }
+ if (changed)
+ resizeEvent(0);
+}
+
+class CardState {
+public:
+ Card *it;
+ Pile *source;
+ double x;
+ double y;
+ double z;
+ bool faceup;
+ bool tookdown;
+ int source_index;
+ CardState() {}
+public:
+ // as every card is only once we can sort after the card.
+ // < is the same as <= in that context. == is different
+ bool operator<(const CardState &rhs) const { return it < rhs.it; }
+ bool operator<=(const CardState &rhs) const { return it <= rhs.it; }
+ bool operator>(const CardState &rhs) const { return it > rhs.it; }
+ bool operator>=(const CardState &rhs) const { return it > rhs.it; }
+ bool operator==(const CardState &rhs) const {
+ return (it == rhs.it && source == rhs.source && x == rhs.x &&
+ y == rhs.y && z == rhs.z && faceup == rhs.faceup
+ && source_index == rhs.source_index && tookdown == rhs.tookdown);
+ }
+ void fillNode(QDomElement &e) const {
+ e.setAttribute("value", it->rank());
+ e.setAttribute("suit", it->suit());
+ e.setAttribute("source", source->index());
+ e.setAttribute("x", x);
+ e.setAttribute("y", y);
+ e.setAttribute("z", z);
+ e.setAttribute("faceup", faceup);
+ e.setAttribute("tookdown", tookdown);
+ e.setAttribute("source_index", source_index);
+ }
+};
+
+typedef class QValueList<CardState> CardStateList;
+
+bool operator==( const State & st1, const State & st2) {
+ return st1.cards == st2.cards && st1.gameData == st2.gameData;
+}
+
+State *Dealer::getState()
+{
+ QCanvasItemList list = canvas()->allItems();
+ State * st = new State;
+
+ for (QCanvasItemList::ConstIterator it = list.begin(); it != list.end(); ++it)
+ {
+ if ((*it)->rtti() == Card::RTTI) {
+ Card *c = dynamic_cast<Card*>(*it);
+ assert(c);
+ CardState s;
+ s.it = c;
+ s.source = c->source();
+ if (!s.source) {
+ kdDebug(11111) << c->name() << " has no parent\n";
+ assert(false);
+ }
+ s.source_index = c->source()->indexOf(c);
+ s.x = c->realX();
+ s.y = c->realY();
+ s.z = c->realZ();
+ s.faceup = c->realFace();
+ s.tookdown = c->takenDown();
+ st->cards.append(s);
+ }
+ }
+ qHeapSort(st->cards);
+
+ // Game specific information
+ st->gameData = getGameState( );
+
+ return st;
+}
+
+void Dealer::setState(State *st)
+{
+ CardStateList * n = &st->cards;
+ QCanvasItemList list = canvas()->allItems();
+
+ for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it)
+ {
+ if ((*it)->rtti() == Pile::RTTI) {
+ Pile *p = dynamic_cast<Pile*>(*it);
+ assert(p);
+ CardList cards = p->cards();
+ for (CardList::Iterator it = cards.begin(); it != cards.end(); ++it)
+ (*it)->setTakenDown(p->target());
+ p->clear();
+ }
+ }
+
+ for (CardStateList::ConstIterator it = n->begin(); it != n->end(); ++it)
+ {
+ Card *c = (*it).it;
+ CardState s = *it;
+ bool target = c->takenDown(); // abused
+ s.source->add(c, s.source_index);
+ c->setVisible(s.source->isVisible());
+ c->setAnimated(false);
+ c->setX(s.x);
+ c->setY(s.y);
+ c->setZ(int(s.z));
+ c->setTakenDown(s.tookdown || (target && !s.source->target()));
+ c->turn(s.faceup);
+ }
+
+ // restore game-specific information
+ setGameState( st->gameData );
+
+ delete st;
+ canvas()->update();
+}
+
+void Dealer::takeState()
+{
+ kdDebug(11111) << "takeState\n";
+
+ State *n = getState();
+
+ if (!undoList.count()) {
+ emit updateMoves();
+ undoList.append(n);
+ } else {
+ State *old = undoList.last();
+
+ if (*old == *n) {
+ delete n;
+ n = 0;
+ } else {
+ emit updateMoves();
+ undoList.append(n);
+ }
+ }
+
+ if (n) {
+ if (isGameWon()) {
+ won();
+ return;
+ }
+ else if (isGameLost() && !toldAboutLostGame) {
+ if ( ahint )
+ ahint->setEnabled( false );
+ if ( ademo )
+ ademo->setEnabled( false );
+ if ( aredeal )
+ aredeal->setEnabled( false );
+ QTimer::singleShot(400, this, SIGNAL(gameLost()));
+ toldAboutLostGame = true;
+ return;
+ }
+ }
+ if (!demoActive() && !waiting())
+ QTimer::singleShot(TIME_BETWEEN_MOVES, this, SLOT(startAutoDrop()));
+
+ emit undoPossible(undoList.count() > 1 && !waiting());
+}
+
+void Dealer::saveGame(QDomDocument &doc) {
+ QDomElement dealer = doc.createElement("dealer");
+ doc.appendChild(dealer);
+ dealer.setAttribute("id", _id);
+ dealer.setAttribute("number", QString::number(gameNumber()));
+ QString data = getGameState();
+ if (!data.isEmpty())
+ dealer.setAttribute("data", data);
+ dealer.setAttribute("moves", QString::number(getMoves()));
+
+ bool taken[1000];
+ memset(taken, 0, sizeof(taken));
+
+ QCanvasItemList list = canvas()->allItems();
+ for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it)
+ {
+ if ((*it)->rtti() == Pile::RTTI) {
+ Pile *p = dynamic_cast<Pile*>(*it);
+ assert(p);
+ if (taken[p->index()]) {
+ kdDebug(11111) << "pile index " << p->index() << " taken twice\n";
+ return;
+ }
+ taken[p->index()] = true;
+
+ QDomElement pile = doc.createElement("pile");
+ pile.setAttribute("index", p->index());
+
+ CardList cards = p->cards();
+ for (CardList::Iterator it = cards.begin();
+ it != cards.end();
+ ++it)
+ {
+ QDomElement card = doc.createElement("card");
+ card.setAttribute("suit", (*it)->suit());
+ card.setAttribute("value", (*it)->rank());
+ card.setAttribute("faceup", (*it)->isFaceUp());
+ card.setAttribute("x", (*it)->realX());
+ card.setAttribute("y", (*it)->realY());
+ card.setAttribute("z", (*it)->realZ());
+ card.setAttribute("name", (*it)->name());
+ pile.appendChild(card);
+ }
+ dealer.appendChild(pile);
+ }
+ }
+
+ /*
+ QDomElement eList = doc.createElement("undo");
+
+ QPtrListIterator<State> it(undoList);
+ for (; it.current(); ++it)
+ {
+ State *n = it.current();
+ QDomElement state = doc.createElement("state");
+ if (!n->gameData.isEmpty())
+ state.setAttribute("data", n->gameData);
+ QDomElement cards = doc.createElement("cards");
+ for (QValueList<CardState>::ConstIterator it2 = n->cards.begin();
+ it2 != n->cards.end(); ++it2)
+ {
+ QDomElement item = doc.createElement("item");
+ (*it2).fillNode(item);
+ cards.appendChild(item);
+ }
+ state.appendChild(cards);
+ eList.appendChild(state);
+ }
+ dealer.appendChild(eList);
+ */
+ // kdDebug(11111) << doc.toString() << endl;
+}
+
+void Dealer::openGame(QDomDocument &doc)
+{
+ unmarkAll();
+ QDomElement dealer = doc.documentElement();
+
+ setGameNumber(dealer.attribute("number").toULong());
+ undoList.clear();
+
+ QDomNodeList piles = dealer.elementsByTagName("pile");
+
+ QCanvasItemList list = canvas()->allItems();
+
+ CardList cards;
+ for (QCanvasItemList::ConstIterator it = list.begin(); it != list.end(); ++it)
+ if ((*it)->rtti() == Card::RTTI)
+ cards.append(static_cast<Card*>(*it));
+
+ Deck::deck()->collectAndShuffle();
+
+ for (QCanvasItemList::Iterator it = list.begin(); it != list.end(); ++it)
+ {
+ if ((*it)->rtti() == Pile::RTTI)
+ {
+ Pile *p = dynamic_cast<Pile*>(*it);
+ assert(p);
+
+ for (uint i = 0; i < piles.count(); ++i)
+ {
+ QDomElement pile = piles.item(i).toElement();
+ if (pile.attribute("index").toInt() == p->index())
+ {
+ QDomNodeList pcards = pile.elementsByTagName("card");
+ for (uint j = 0; j < pcards.count(); ++j)
+ {
+ QDomElement card = pcards.item(j).toElement();
+ Card::Suit s = static_cast<Card::Suit>(card.attribute("suit").toInt());
+ Card::Rank v = static_cast<Card::Rank>(card.attribute("value").toInt());
+
+ for (CardList::Iterator it2 = cards.begin();
+ it2 != cards.end(); ++it2)
+ {
+ if ((*it2)->suit() == s && (*it2)->rank() == v) {
+ if (QString((*it2)->name()) == "Diamonds Eight") {
+ kdDebug(11111) << i << " " << j << endl;
+ }
+ p->add(*it2);
+ (*it2)->setAnimated(false);
+ (*it2)->turn(card.attribute("faceup").toInt());
+ (*it2)->setX(card.attribute("x").toInt());
+ (*it2)->setY(card.attribute("y").toInt());
+ (*it2)->setZ(card.attribute("z").toInt());
+ (*it2)->setVisible(p->isVisible());
+ cards.remove(it2);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ setGameState( dealer.attribute("data") );
+
+ if (undoList.count() > 1) {
+ setState(undoList.take(undoList.count() - 1));
+ takeState(); // copying it again
+ emit undoPossible(undoList.count() > 1);
+ }
+
+ emit updateMoves();
+ takeState();
+}
+
+void Dealer::undo()
+{
+ unmarkAll();
+ stopDemo();
+ if (undoList.count() > 1) {
+ undoList.removeLast(); // the current state
+ setState(undoList.take(undoList.count() - 1));
+ emit updateMoves();
+ takeState(); // copying it again
+ emit undoPossible(undoList.count() > 1);
+ if ( toldAboutLostGame ) { // everything's possible again
+ if ( ahint )
+ ahint->setEnabled( true );
+ if ( ademo )
+ ademo->setEnabled( true );
+ toldAboutLostGame = false;
+ }
+ }
+}
+
+Pile *Dealer::findTarget(Card *c)
+{
+ if (!c)
+ return 0;
+
+ CardList empty;
+ empty.append(c);
+ for (PileList::ConstIterator it = piles.begin(); it != piles.end(); ++it)
+ {
+ if (!(*it)->target())
+ continue;
+ if ((*it)->legalAdd(empty))
+ return *it;
+ }
+ return 0;
+}
+
+void Dealer::setWaiting(bool w)
+{
+ if (w)
+ _waiting++;
+ else
+ _waiting--;
+ emit undoPossible(!waiting());
+ kdDebug(11111) << "setWaiting " << w << " " << _waiting << endl;
+}
+
+void Dealer::setAutoDropEnabled(bool a)
+{
+ _autodrop = a;
+ QTimer::singleShot(TIME_BETWEEN_MOVES, this, SLOT(startAutoDrop()));
+}
+
+bool Dealer::startAutoDrop()
+{
+ if (!autoDrop())
+ return false;
+
+ QCanvasItemList list = canvas()->allItems();
+
+ for (QCanvasItemList::ConstIterator it = list.begin(); it != list.end(); ++it)
+ if ((*it)->animated()) {
+ QTimer::singleShot(TIME_BETWEEN_MOVES, this, SLOT(startAutoDrop()));
+ return true;
+ }
+
+ kdDebug(11111) << "startAutoDrop\n";
+
+ unmarkAll();
+ clearHints();
+ getHints();
+ for (HintList::ConstIterator it = hints.begin(); it != hints.end(); ++it) {
+ MoveHint *mh = *it;
+ if (mh->pile()->target() && mh->dropIfTarget() && !mh->card()->takenDown()) {
+ setWaiting(true);
+ Card *t = mh->card();
+ CardList cards = mh->card()->source()->cards();
+ while (cards.count() && cards.first() != t) cards.remove(cards.begin());
+ t->setAnimated(false);
+ t->turn(true);
+ int x = int(t->x());
+ int y = int(t->y());
+ t->source()->moveCards(cards, mh->pile());
+ t->move(x, y);
+ kdDebug(11111) << "autodrop " << t->name() << endl;
+ t->moveTo(int(t->source()->x()), int(t->source()->y()), int(t->z()), STEPS_AUTODROP);
+ connect(t, SIGNAL(stoped(Card*)), SLOT(waitForAutoDrop(Card*)));
+ return true;
+ }
+ }
+ clearHints();
+ return false;
+}
+
+void Dealer::waitForAutoDrop(Card * c) {
+ kdDebug(11111) << "waitForAutoDrop " << c->name() << endl;
+ setWaiting(false);
+ c->disconnect();
+ takeState();
+}
+
+long Dealer::gameNumber() const
+{
+ return gamenumber;
+}
+
+void Dealer::setGameNumber(long gmn)
+{
+ // Deal in the range of 1 to INT_MAX.
+ gamenumber = ((gmn < 1) ? 1 : ((gmn > 0x7FFFFFFF) ? 0x7FFFFFFF : gmn));
+}
+
+void Dealer::addPile(Pile *p)
+{
+ piles.append(p);
+}
+
+void Dealer::removePile(Pile *p)
+{
+ piles.remove(p);
+}
+
+void Dealer::stopDemo()
+{
+ kdDebug(11111) << "stopDemo " << waiting() << " " << stop_demo_next << endl;
+ if (waiting()) {
+ stop_demo_next = true;
+ return;
+ } else stop_demo_next = false;
+
+ if (towait == (Card*)-1)
+ towait = 0;
+
+ if (towait) {
+ towait->disconnect();
+ towait = 0;
+ }
+ demotimer->stop();
+ if (ademo)
+ ademo->setChecked(false);
+}
+
+bool Dealer::demoActive() const
+{
+ return (towait || demotimer->isActive());
+}
+
+void Dealer::toggleDemo()
+{
+ if (demoActive()) {
+ stopDemo();
+ } else
+ demo();
+}
+
+class CardPtr
+{
+ public:
+ Card *ptr;
+};
+
+bool operator <(const CardPtr &p1, const CardPtr &p2)
+{
+ return ( p1.ptr->z() < p2.ptr->z() );
+}
+
+void Dealer::won()
+{
+ if (_won)
+ return;
+ _won = true;
+
+ // update score, 'win' in demo mode also counts (keep it that way?)
+ { // wrap in own scope to make KConfigGroupSave work
+ KConfig *config = kapp->config();
+ KConfigGroupSaver kcs(config, scores_group);
+ unsigned int n = config->readUnsignedNumEntry(QString("won%1").arg(_id),0) + 1;
+ config->writeEntry(QString("won%1").arg(_id),n);
+ n = config->readUnsignedNumEntry(QString("winstreak%1").arg(_id),0) + 1;
+ config->writeEntry(QString("winstreak%1").arg(_id),n);
+ unsigned int m = config->readUnsignedNumEntry(QString("maxwinstreak%1").arg(_id),0);
+ if (n>m)
+ config->writeEntry(QString("maxwinstreak%1").arg(_id),n);
+ config->writeEntry(QString("loosestreak%1").arg(_id),0);
+ }
+
+ // sort cards by increasing z
+ QCanvasItemList list = canvas()->allItems();
+ QValueList<CardPtr> cards;
+ for (QCanvasItemList::ConstIterator it=list.begin(); it!=list.end(); ++it)
+ if ((*it)->rtti() == Card::RTTI) {
+ CardPtr p;
+ p.ptr = dynamic_cast<Card*>(*it);
+ assert(p.ptr);
+ cards.push_back(p);
+ }
+ qHeapSort(cards);
+
+ // disperse the cards everywhere
+ QRect can(0, 0, canvas()->width(), canvas()->height());
+ QValueList<CardPtr>::ConstIterator it = cards.begin();
+ for (; it != cards.end(); ++it) {
+ (*it).ptr->turn(true);
+ QRect p(0, 0, (*it).ptr->width(), (*it).ptr->height());
+ int x, y;
+ do {
+ x = 3*canvas()->width()/2 - kapp->random() % (canvas()->width() * 2);
+ y = 3*canvas()->height()/2 - (kapp->random() % (canvas()->height() * 2));
+ p.moveTopLeft(QPoint(x, y));
+ } while (can.intersects(p));
+
+ (*it).ptr->moveTo( x, y, 0, STEPS_WON);
+ }
+
+ bool demo = demoActive();
+ stopDemo();
+ canvas()->update();
+ emit gameWon(demo);
+}
+
+MoveHint *Dealer::chooseHint()
+{
+ if (hints.isEmpty())
+ return 0;
+
+ for (HintList::ConstIterator it = hints.begin(); it != hints.end(); ++it)
+ {
+ if ((*it)->pile()->target() && (*it)->dropIfTarget())
+ return *it;
+ }
+
+ return hints[randseq.getLong(hints.count())];
+}
+
+void Dealer::demo() {
+ if (waiting())
+ return;
+
+ if (stop_demo_next) {
+ stopDemo();
+ return;
+ }
+ stop_demo_next = false;
+ unmarkAll();
+ towait = (Card*)-1;
+ clearHints();
+ getHints();
+ demotimer->stop();
+
+ MoveHint *mh = chooseHint();
+ if (mh) {
+ // assert(mh->card()->source()->legalRemove(mh->card()));
+
+ CardList empty;
+ CardList cards = mh->card()->source()->cards();
+ bool after = false;
+ for (CardList::Iterator it = cards.begin(); it != cards.end(); ++it) {
+ if (*it == mh->card())
+ after = true;
+ if (after)
+ empty.append(*it);
+ }
+
+ assert(!empty.isEmpty());
+
+ int *oldcoords = new int[2*empty.count()];
+ int i = 0;
+
+ for (CardList::Iterator it = empty.begin(); it != empty.end(); ++it) {
+ Card *t = *it;
+ Q_ASSERT(!t->animated());
+ t->setAnimated(false);
+ t->turn(true);
+ oldcoords[i++] = int(t->realX());
+ oldcoords[i++] = int(t->realY());
+ }
+
+ assert(mh->card()->source() != mh->pile());
+ // assert(mh->pile()->legalAdd(empty));
+
+ mh->card()->source()->moveCards(empty, mh->pile());
+
+ i = 0;
+
+ for (CardList::Iterator it = empty.begin(); it != empty.end(); ++it) {
+ Card *t = *it;
+ int x1 = oldcoords[i++];
+ int y1 = oldcoords[i++];
+ int x2 = int(t->realX());
+ int y2 = int(t->realY());
+ t->move(x1, y1);
+ t->moveTo(x2, y2, int(t->z()), STEPS_DEMO);
+ }
+
+ delete [] oldcoords;
+
+ newDemoMove(mh->card());
+
+ } else {
+ Card *t = demoNewCards();
+ if (t) {
+ newDemoMove(t);
+ } else if (isGameWon()) {
+ canvas()->update();
+ emit gameWon(true);
+ return;
+ } else
+ stopDemo();
+ }
+
+ takeState();
+}
+
+Card *Dealer::demoNewCards()
+{
+ return 0;
+}
+
+void Dealer::newDemoMove(Card *m)
+{
+ towait = m;
+ connect(m, SIGNAL(stoped(Card*)), SLOT(waitForDemo(Card*)));
+}
+
+void Dealer::waitForDemo(Card *t)
+{
+ if (t == (Card*)-1)
+ return;
+ if (towait != t)
+ return;
+ t->disconnect();
+ towait = 0;
+ demotimer->start(250, true);
+}
+
+bool Dealer::isGameWon() const
+{
+ for (PileList::ConstIterator it = piles.begin(); it != piles.end(); ++it)
+ {
+ if (!(*it)->target() && !(*it)->isEmpty())
+ return false;
+ }
+ return true;
+}
+
+bool Dealer::isGameLost() const
+{
+ return false;
+}
+
+bool Dealer::checkRemove( int, const Pile *, const Card *) const {
+ return true;
+}
+
+bool Dealer::checkAdd( int, const Pile *, const CardList&) const {
+ return true;
+}
+
+void Dealer::drawPile(KPixmap &pixmap, Pile *pile, bool selected)
+{
+ QPixmap bg = myCanvas.backgroundPixmap();
+ QRect bounding(int(pile->x()), int(pile->y()), cardMap::CARDX(), cardMap::CARDY());
+
+ pixmap.resize(bounding.width(), bounding.height());
+ pixmap.fill(Qt::white);
+
+ if (!bg.isNull()) {
+ for (int x=bounding.x()/bg.width();
+ x<(bounding.x()+bounding.width()+bg.width()-1)/bg.width(); x++)
+ {
+ for (int y=bounding.y()/bg.height();
+ y<(bounding.y()+bounding.height()+bg.height()-1)/bg.height(); y++)
+ {
+ int sx = 0;
+ int sy = 0;
+ int dx = x*bg.width()-bounding.x();
+ int dy = y*bg.height()-bounding.y();
+ int w = bg.width();
+ int h = bg.height();
+ if (dx < 0) {
+ sx = -dx;
+ dx = 0;
+ }
+ if (dy < 0) {
+ sy = -dy;
+ dy = 0;
+ }
+ bitBlt(&pixmap, dx, dy, &bg,
+ sx, sy, w, h, Qt::CopyROP, true);
+ }
+ }
+ }
+
+
+ float s = -0.4;
+ float n = -0.3;
+
+ int mid = QMAX( QMAX(midColor().red(), midColor().green()), midColor().blue());
+
+ // if it's too dark - light instead of dark
+ if (mid < 120) {
+ s *= -1;
+ n = 0.4;
+ }
+
+ KPixmapEffect::intensity(pixmap, selected ? s : n);
+}
+
+int Dealer::freeCells() const
+{
+ int n = 0;
+ for (PileList::ConstIterator it = piles.begin(); it != piles.end(); ++it)
+ if ((*it)->isEmpty() && !(*it)->target())
+ n++;
+ return n;
+}
+
+void Dealer::setAnchorName(const QString &name)
+{
+ kdDebug(11111) << "setAnchorname " << name << endl;
+ ac = name;
+}
+
+QString Dealer::anchorName() const { return ac; }
+
+void Dealer::wheelEvent( QWheelEvent *e )
+{
+ QWheelEvent ce( viewport()->mapFromGlobal( e->globalPos() ),
+ e->globalPos(), e->delta(), e->state());
+ viewportWheelEvent(&ce);
+ if ( !ce.isAccepted() ) {
+ if ( e->orientation() == Horizontal && hScrollBarMode () == AlwaysOn )
+ QApplication::sendEvent( horizontalScrollBar(), e);
+ else if (e->orientation() == Vertical && vScrollBarMode () == AlwaysOn )
+ QApplication::sendEvent( verticalScrollBar(), e);
+ } else {
+ e->accept();
+ }
+}
+
+void Dealer::countGame()
+{
+ if ( !_gameRecorded ) {
+ kdDebug(11111) << "counting game as played." << endl;
+ KConfig *config = kapp->config();
+ KConfigGroupSaver kcs(config, scores_group);
+ unsigned int Total = config->readUnsignedNumEntry(QString("total%1").arg(_id),0);
+ ++Total;
+ config->writeEntry(QString("total%1").arg(_id),Total);
+ _gameRecorded = true;
+ }
+}
+
+void Dealer::countLoss()
+{
+ if ( _gameRecorded ) {
+ // update score
+ KConfig *config = kapp->config();
+ KConfigGroupSaver kcs(config, scores_group);
+ unsigned int n = config->readUnsignedNumEntry(QString("loosestreak%1").arg(_id),0) + 1;
+ config->writeEntry(QString("loosestreak%1").arg(_id),n);
+ unsigned int m = config->readUnsignedNumEntry(QString("maxloosestreak%1").arg(_id),0);
+ if (n>m)
+ config->writeEntry(QString("maxloosestreak%1").arg(_id),n);
+ config->writeEntry(QString("winstreak%1").arg(_id),0);
+ }
+}
+
+#include "dealer.moc"
diff --git a/kpat/dealer.h b/kpat/dealer.h
new file mode 100644
index 00000000..c5593cbd
--- /dev/null
+++ b/kpat/dealer.h
@@ -0,0 +1,228 @@
+#ifndef _DEALER_H_
+#define _DEALER_H_
+
+#include "pile.h"
+#include "hint.h"
+#include <krandomsequence.h>
+
+class QDomDocument;
+class KMainWindow;
+class Dealer;
+class DealerInfo;
+class KAction;
+class KSelectAction;
+class KToggleAction;
+class KPixmap;
+
+class DealerInfoList {
+public:
+ static DealerInfoList *self();
+ void add(DealerInfo *);
+
+ const QValueList<DealerInfo*> games() const { return list; }
+private:
+ QValueList<DealerInfo*> list;
+ static DealerInfoList *_self;
+};
+
+class DealerInfo {
+public:
+ DealerInfo(const char *_name, int _index)
+ : name(_name),
+ gameindex(_index)
+{
+ DealerInfoList::self()->add(this);
+}
+ const char *name;
+ uint gameindex;
+ virtual Dealer *createGame(KMainWindow *parent) = 0;
+};
+
+class CardState;
+
+typedef QValueList<CardState> CardStateList;
+
+struct State
+{
+ CardStateList cards;
+ QString gameData;
+};
+
+
+/***************************************************************
+
+ Dealer -- abstract base class of all varieties of patience
+
+***************************************************************/
+class Dealer: public QCanvasView
+{
+ Q_OBJECT
+
+public:
+
+ Dealer( KMainWindow* parent = 0, const char* name = 0 );
+ virtual ~Dealer();
+
+ static const Dealer *instance();
+
+ void enlargeCanvas(QCanvasRectangle *c);
+ void setGameNumber(long gmn);
+ long gameNumber() const;
+
+ virtual bool isGameWon() const;
+ virtual bool isGameLost() const;
+
+ void setViewSize(const QSize &size);
+
+ void addPile(Pile *p);
+ void removePile(Pile *p);
+
+ virtual bool checkRemove( int checkIndex, const Pile *c1, const Card *c) const;
+ virtual bool checkAdd ( int checkIndex, const Pile *c1, const CardList& c2) const;
+ virtual bool checkPrefering( int checkIndex, const Pile *c1, const CardList& c2) const;
+
+ virtual Card *demoNewCards();
+
+ virtual void setupActions();
+
+ bool demoActive() const;
+
+ void drawPile(KPixmap &, Pile *p, bool selected);
+
+ QColor midColor() const { return _midcolor; }
+ void setBackgroundPixmap(const QPixmap &background, const QColor &midcolor);
+
+ void saveGame(QDomDocument &doc);
+ void openGame(QDomDocument &doc);
+
+ void setGameId(int id) { _id = id; }
+ int gameId() const { return _id; }
+
+ void setTakeTargetForHints(bool e) { takeTargets = e; }
+ bool takeTargetForHints() const { return takeTargets; }
+
+ bool isMoving(Card *c) const;
+
+ virtual QSize minimumCardSize() const;
+ virtual void resizeEvent(QResizeEvent *);
+
+ int freeCells() const;
+
+ QString anchorName() const;
+ void setAnchorName(const QString &name);
+
+ void setAutoDropEnabled(bool a);
+ bool autoDrop() const { return _autodrop; }
+
+ int getMoves() const { return undoList.count(); }
+
+public slots:
+
+ // restart is pure virtual, so we need something else
+ virtual void startNew();
+ void undo();
+ virtual void takeState();
+ virtual bool startAutoDrop();
+ void hint();
+ void slotTakeState(Card *c);
+
+signals:
+ void undoPossible(bool poss);
+ void gameWon(bool withhelp);
+ void gameLost();
+ void saveGame(); // emergency
+ void gameInfo(const QString &info);
+ void updateMoves();
+
+public slots:
+ virtual void demo();
+ void waitForDemo(Card *);
+ void toggleDemo();
+ virtual void stopDemo();
+ void waitForAutoDrop(Card *);
+
+protected:
+
+ enum { None = 0, Hint = 1, Demo = 2, Redeal = 4 } Actions;
+
+ void setActions(int actions) { myActions = actions; }
+ int actions() const { return myActions; }
+
+ virtual void restart() = 0;
+
+ virtual void contentsMousePressEvent(QMouseEvent* e);
+ virtual void contentsMouseMoveEvent( QMouseEvent* );
+ virtual void contentsMouseReleaseEvent( QMouseEvent* );
+ virtual void contentsMouseDoubleClickEvent( QMouseEvent* );
+ virtual void wheelEvent( QWheelEvent *e );
+
+ void unmarkAll();
+ void mark(Card *c);
+ Pile *findTarget(Card *c);
+ virtual bool cardClicked(Card *);
+ virtual void pileClicked(Pile *);
+ virtual bool cardDblClicked(Card *);
+ void won();
+
+ virtual void getHints();
+ void newHint(MoveHint *mh);
+ void clearHints();
+ // it's not const because it changes the random seed
+ virtual MoveHint *chooseHint();
+
+ KMainWindow *parent() const;
+
+ bool waiting() const { return _waiting != 0; }
+ void setWaiting(bool w);
+
+protected:
+ PileList piles;
+
+ State *getState();
+ void setState(State *);
+
+ // reimplement this to add game-specific information in the state structure
+ virtual QString getGameState() const { return QString::null; }
+ // reimplement this to use the game-specific information from the state structure
+ virtual void setGameState( const QString & ) {}
+
+ virtual void newDemoMove(Card *m);
+
+ bool moved;
+ CardList movingCards;
+ QCanvasItemList marked;
+ QPoint moving_start;
+ Dealer( Dealer& ); // don't allow copies or assignments
+ void operator = ( Dealer& ); // don't allow copies or assignments
+ QCanvas myCanvas;
+ QSize minsize;
+ QSize viewsize;
+ QPtrList<State> undoList;
+ long gamenumber;
+ QValueList<MoveHint*> hints;
+ Card *towait;
+ QTimer *demotimer;
+ int myActions;
+ bool toldAboutLostGame;
+
+ KToggleAction *ademo;
+ KAction *ahint, *aredeal;
+
+ KRandomSequence randseq;
+ QColor _midcolor;
+ Q_UINT32 _id;
+ bool takeTargets;
+ bool _won;
+ int _waiting;
+ bool stop_demo_next;
+ QString ac;
+ static Dealer *s_instance;
+ bool _autodrop;
+ bool _gameRecorded;
+
+private:
+ void countLoss();
+ void countGame();
+};
+
+#endif
diff --git a/kpat/deck.cpp b/kpat/deck.cpp
new file mode 100644
index 00000000..c2b6e0e5
--- /dev/null
+++ b/kpat/deck.cpp
@@ -0,0 +1,154 @@
+#include <kdebug.h>
+#include "deck.h"
+#include "dealer.h"
+#include <time.h>
+#include <assert.h>
+
+const int NumberOfCards = 52;
+
+
+Deck *Deck::my_deck = 0;
+
+
+Deck::Deck( Dealer* parent, int m, int s )
+ : Pile( 0, parent ), mult( m )
+{
+ _deck = new Card * [mult*NumberOfCards];
+ Q_CHECK_PTR (_deck);
+
+ // only allow 1, 2, or 4 suits
+ if ( s == 1 || s == 2 )
+ suits = s;
+ else
+ suits = 4;
+
+ makedeck();
+ addToDeck();
+ shuffle();
+
+ setAddFlags(Pile::disallow);
+ setRemoveFlags(Pile::disallow);
+}
+
+
+Deck::~Deck()
+{
+ for (uint i=0; i < mult*NumberOfCards; i++) {
+ delete _deck[i];
+ }
+ m_cards.clear();
+ delete [] _deck;
+}
+
+
+// ----------------------------------------------------------------
+
+
+Deck *Deck::new_deck( Dealer *parent, int m, int s )
+{
+ my_deck = new Deck(parent, m, s);
+ return my_deck;
+}
+
+
+void Deck::makedeck()
+{
+ int i=0;
+
+ show();
+ for ( uint m = 0; m < mult; m++)
+ {
+ for ( int r = Card::Ace; r <= Card::King; r++)
+ {
+ for ( int s = Card::Spades-1; s >= Card::Clubs-1 ; s--)
+ {
+ _deck[i] = new Card(static_cast<Card::Rank>(r),
+ static_cast<Card::Suit>(Card::Spades - (s % suits)),
+ dealer()->canvas());
+ _deck[i]->move(x(), y());
+ i++;
+ }
+ }
+ }
+}
+
+
+void Deck::collectAndShuffle()
+{
+ addToDeck();
+ shuffle();
+}
+
+
+Card* Deck::nextCard()
+{
+ CardList::Iterator c;
+
+ c = m_cards.fromLast(); // Dealing from bottom of deck ....
+ if ( c != m_cards.end() ) {
+ return *c;
+ } else
+ return 0;
+}
+
+
+// ----------------------------------------------------------------
+
+
+static long pseudoRandomSeed = 0;
+
+static void pseudoRandom_srand(long seed)
+{
+ pseudoRandomSeed=seed;
+}
+
+
+// Documented as in
+// http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q28150
+//
+
+static long pseudoRandom_random() {
+ pseudoRandomSeed = 214013*pseudoRandomSeed+2531011;
+ return (pseudoRandomSeed >> 16) & 0x7fff;
+}
+
+
+// Shuffle deck, assuming all cards are in m_cards
+
+void Deck::shuffle()
+{
+
+ assert(m_cards.count() == uint(mult*NumberOfCards));
+
+ assert(dealer()->gameNumber() >= 0);
+ pseudoRandom_srand(dealer()->gameNumber());
+
+ kdDebug(11111) << "first card " << m_cards[0]->name() << " " << dealer()->gameNumber() << endl;
+
+ Card* t;
+ long z;
+ int left = mult*NumberOfCards;
+ for (uint i = 0; i < mult*NumberOfCards; i++) {
+ z = pseudoRandom_random() % left;
+ t = m_cards[z];
+ m_cards[z] = m_cards[left-1];
+ m_cards[left-1] = t;
+ left--;
+ }
+}
+
+
+// add cards in deck[] to Deck
+// FIXME: Rename to collectCards()
+
+void Deck::addToDeck()
+{
+ clear();
+
+ for (uint i = 0; i < mult*NumberOfCards; i++) {
+ _deck[i]->setTakenDown(false);
+ add( _deck[i], true, false );
+ }
+}
+
+
diff --git a/kpat/deck.h b/kpat/deck.h
new file mode 100644
index 00000000..f5239fe0
--- /dev/null
+++ b/kpat/deck.h
@@ -0,0 +1,46 @@
+#ifndef _DECK_H_
+#define _DECK_H_
+
+#include "pile.h"
+class dealer;
+
+/***************************************
+
+ Deck (Pile with id 0) -- create and shuffle 52 cards
+
+**************************************/
+class Deck: public Pile
+{
+
+private:
+ Deck( Dealer* parent = 0, int m = 1, int s = 4 );
+ virtual ~Deck();
+
+public:
+ static Deck *new_deck( Dealer *parent = 0, int m = 1, int s = 4 );
+ static Deck *deck() { return my_deck; }
+
+ static const long n;
+
+ void collectAndShuffle();
+
+ Card* nextCard();
+
+ uint decksNum() const { return mult; }
+
+private: // functions
+
+ void makedeck();
+ void addToDeck();
+ void shuffle();
+
+private:
+
+ uint mult;
+ uint suits;
+ Card** _deck;
+
+ static Deck *my_deck;
+};
+
+#endif
diff --git a/kpat/fortyeight.cpp b/kpat/fortyeight.cpp
new file mode 100644
index 00000000..1d867378
--- /dev/null
+++ b/kpat/fortyeight.cpp
@@ -0,0 +1,205 @@
+#include "fortyeight.h"
+#include <klocale.h>
+#include <kdebug.h>
+#include "deck.h"
+#include <assert.h>
+#include "cardmaps.h"
+
+HorLeftPile::HorLeftPile( int _index, Dealer* parent)
+ : Pile(_index, parent)
+{
+ // TODO: create a pile that moves the cards together when filling space
+ setHSpread( cardMap::CARDX() / 11 + 1 );
+}
+
+QSize HorLeftPile::cardOffset( bool _spread, bool, const Card *) const
+{
+ if (_spread)
+ return QSize(-hspread(), 0);
+
+ return QSize(0, 0);
+}
+
+void HorLeftPile::initSizes()
+{
+ Pile::initSizes();
+ setHSpread( cardMap::CARDX() / 11 + 1 );
+}
+
+
+Fortyeight::Fortyeight( KMainWindow* parent, const char* name)
+ : Dealer(parent,name)
+{
+ deck = Deck::new_deck(this, 2);
+
+ const int dist_x = cardMap::CARDX() * 11 / 10 + 1;
+ const int dist_y = cardMap::CARDY() * 11 / 10 + 1;
+
+ connect(deck, SIGNAL(clicked(Card*)), SLOT(deckClicked(Card*)));
+ deck->move(10 + cardMap::CARDX() * 82 / 10, 10 + cardMap::CARDX() * 56 / 10);
+ deck->setZ(20);
+
+ pile = new HorLeftPile(20, this);
+ pile->setAddFlags(Pile::addSpread | Pile::disallow);
+ pile->move(10 + cardMap::CARDX() * 69 / 10, 10 + cardMap::CARDX() * 56 / 10 );
+
+ for (int i = 0; i < 8; i++) {
+
+ target[i] = new Pile(9 + i, this);
+ target[i]->move(8+dist_x*i, 10);
+ target[i]->setType(Pile::KlondikeTarget);
+
+ stack[i] = new Pile(1 + i, this);
+ stack[i]->move(8+dist_x*i, 10 + dist_y);
+ stack[i]->setAddFlags(Pile::addSpread);
+ stack[i]->setRemoveFlags(Pile::autoTurnTop);
+ stack[i]->setCheckIndex(1);
+ stack[i]->setSpread(stack[i]->spread() * 3 / 4);
+ }
+
+ setActions(Dealer::Hint | Dealer::Demo);
+}
+
+//-------------------------------------------------------------------------//
+
+void Fortyeight::restart()
+{
+ lastdeal = false;
+ deck->collectAndShuffle();
+ deal();
+}
+
+void Fortyeight::deckClicked(Card *)
+{
+ if (deck->isEmpty()) {
+ if (lastdeal)
+ return;
+ lastdeal = true;
+ while (!pile->isEmpty()) {
+ Card *c = pile->at(pile->cardsLeft()-1);
+ c->setAnimated(false);
+ deck->add(c, true, false);
+ }
+ }
+ Card *c = deck->nextCard();
+ pile->add(c, true, true);
+ int x = int(c->x());
+ int y = int(c->y());
+ c->move(deck->x(), deck->y());
+ c->flipTo(x, y, 8);
+}
+
+Card *Fortyeight::demoNewCards()
+{
+ if (deck->isEmpty() && lastdeal)
+ return 0;
+ deckClicked(0);
+ return pile->top();
+}
+
+bool Fortyeight::checkAdd(int, const Pile *c1, const CardList &c2) const
+{
+ if (c1->isEmpty())
+ return true;
+
+ // ok if in sequence, same suit
+ return (c1->top()->suit() == c2.first()->suit())
+ && (c1->top()->rank() == (c2.first()->rank()+1));
+}
+
+void Fortyeight::deal()
+{
+ for (int r = 0; r < 4; r++)
+ {
+ for (int column = 0; column < 8; column++)
+ {
+ if (false) { // doesn't look
+ stack[column]->add(deck->nextCard(), true, true);
+ stack[column]->top()->turn(true);
+ } else {
+ stack[column]->add(deck->nextCard(), false, true);
+ }
+ }
+ }
+ pile->add(deck->nextCard(), false, false);
+}
+
+QString Fortyeight::getGameState() const
+{
+ return QString::number(lastdeal);
+}
+
+void Fortyeight::setGameState( const QString &s )
+{
+ lastdeal = s.toInt();
+}
+
+bool Fortyeight::isGameLost() const
+{
+ kdDebug(11111) << "isGameLost ?" << endl;
+ if(!lastdeal)
+ return false;
+ if(!deck->isEmpty())
+ return false;
+
+ Card *c;
+ for(int i=0; i < 8; i++)
+ {
+ if(stack[i]->isEmpty())
+ return false;
+
+ c=stack[i]->top();
+
+ if(c->rank() == Card::Ace)
+ return false;
+
+ if(!pile->isEmpty()) {
+ if(pile->top()->suit() == c->suit() &&
+ pile->top()->rank()+1 == c->rank())
+ return false;
+
+ if ( !target[i]->isEmpty() &&
+ pile->top()->suit() == target[i]->top()->suit() &&
+ pile->top()->rank() == target[i]->top()->rank()+1)
+ return false;
+ }
+ for(int j=0; j <8;j++){
+ if(target[j]->isEmpty())
+ continue;
+ if(c->suit() == target[j]->top()->suit() &&
+ c->rank()-1 ==target[j]->top()->rank())
+ return false;
+ }
+ for(int j=1; j < 8; j++) {
+ int k=(i+j) % 8;
+ if (stack[k]->isEmpty())
+ continue;
+ if(c->suit() == stack[k]->top()->suit() &&
+ c->rank()+1 ==stack[k]->top()->rank()){
+ int indexi=stack[i]->indexOf(c);
+ if(indexi==0)
+ return false;
+ Card *c2=stack[i]->at(indexi-1);
+ if(c2->rank()!=stack[k]->top()->rank() ||
+ c2->suit()!=stack[k]->top()->suit())
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+static class LocalDealerInfo8 : public DealerInfo
+{
+public:
+ LocalDealerInfo8() : DealerInfo(I18N_NOOP("Forty && &Eight"), 8) {}
+ virtual Dealer *createGame(KMainWindow *parent) { return new Fortyeight(parent); }
+} ldi9;
+
+//-------------------------------------------------------------------------//
+
+#include "fortyeight.moc"
+
+//-------------------------------------------------------------------------//
+
diff --git a/kpat/fortyeight.h b/kpat/fortyeight.h
new file mode 100644
index 00000000..858e6e15
--- /dev/null
+++ b/kpat/fortyeight.h
@@ -0,0 +1,46 @@
+#ifndef _FORTY_EIGHT_H
+#define _FORTY_EIGHT_H
+
+#include "dealer.h"
+
+class HorLeftPile : public Pile
+{
+ Q_OBJECT
+
+public:
+ HorLeftPile( int _index, Dealer* parent = 0);
+ virtual QSize cardOffset( bool _spread, bool _facedown, const Card *before) const;
+ virtual void initSizes();
+};
+
+class Fortyeight : public Dealer
+{
+ Q_OBJECT
+
+public:
+ Fortyeight( KMainWindow* parent=0, const char* name=0);
+ virtual bool isGameLost() const;
+
+public slots:
+ void deal();
+ virtual void restart();
+ void deckClicked(Card *c);
+
+protected:
+ virtual bool checkAdd( int checkIndex, const Pile *c1, const CardList& c2) const;
+ virtual Card *demoNewCards();
+ virtual QString getGameState() const;
+ virtual void setGameState( const QString & stream );
+
+private:
+ Pile *stack[8];
+ Pile *target[8];
+ HorLeftPile *pile;
+ Deck *deck;
+ bool lastdeal;
+};
+
+#endif
+
+
+//-------------------------------------------------------------------------//
diff --git a/kpat/freecell-solver/CREDITS b/kpat/freecell-solver/CREDITS
new file mode 100644
index 00000000..7ce9e216
--- /dev/null
+++ b/kpat/freecell-solver/CREDITS
@@ -0,0 +1,57 @@
+Shlomi Fish (me) - doing most of the work on Freecell Solver.
+
+Eric Warmenhoven - sending a program that generates the board of GNOME
+Freecell.
+
+Hai Huang - noting several boards of Microsoft Freecell that could not be
+solved by Freecell Solver.
+
+Magnus Reftel - noting the correct procedure for calculating how many cards
+can be moved as a function of the free freecells and free stacks.
+
+Colin Plumb - writing the MD5 code.
+
+Ron Rivest - inventing the MD5 hashing algorithm.
+
+Jim Horne - supplying the shuffling algorithm for
+Microsoft Freecell/Freecell Pro.
+
+Tom Holroyd - sending several Seahaven Towers games which Freecell Solver
+was unable to solve, thus making me improve the algorithm.
+
+Markus F. X. J. Oberhumer - writing PySol on whose code the board generation
+program for it is based. Also, contributing some patches.
+
+Justin-Heyes Jones - wrote a nice introduction to the A* algorithm, and
+wrote the basis for the pqueue.c code.
+
+Stephan Kulow - integrated Freecell Solver into the kpat Solitaire suite for
+KDE 2.1 and onwards; reported several bugs and memory leaks.
+
+Michael Keller - Contributing some useful input about some minor features
+lacking and the Spades/Clubs mix-up.
+
+GeYong - He created Freecell Tool, whose randomized scan provided
+inspiration for the random-DFS scan of Freecell Solver.
+
+Adrian Ettlinger - Integrating Freecell Solver into "Freecell Pro", and
+contributing some input.
+
+The perl 5.x Hackers - I copied its hash function.
+
+Gergeley Kontra - wrote a Vim script to align text which I used.
+
+Bob Jenkins - wrote the lookup2 hash function, which I now use as the
+primary hash.
+(check http://burtleburtle.net/bob/hash/)
+
+Tzafrir Cohen - His "RPM" lecture provided help in creating the RPM
+Spec.
+
+Yotam Rubin - Preparing an initial Debian Package.
+
+Risko Gergely - Maintaining the current Debian Package.
+
+Chris Moore - Pointing to an out-of-date comment regarding the
+MAX_NUM_CARDS_IN_A_STACK which I updated.
+
diff --git a/kpat/freecell-solver/INSTALL b/kpat/freecell-solver/INSTALL
new file mode 100644
index 00000000..9b718633
--- /dev/null
+++ b/kpat/freecell-solver/INSTALL
@@ -0,0 +1,70 @@
+INSTALL file for Freecell Solver
+================================
+
+Quick and Dirty Compilation
+---------------------------
+
+Usually typing "./configure" followed by "make" and "make install" will
+build and install "fc-solve" which is the Freecell Solver executable for you.
+
+It will also build and install the board generation program, more
+information about which can be found in the "board_gen" sub-directory of
+this distribution.
+
+Changing the Maximal number of Freecells or Stacks or Cards per Stack
+---------------------------------------------------------------------
+
+The following parameters to the "configure" script which accept an argument
+control the hard-coded parameters of the Freecell Solver executables:
+
+"--enable-max-num-freecells=$NUM" - The maximal number of freecells
+
+"--enable-max-num-stacks=$NUM" - The maximal number of stacks
+
+"--enable-max-num-initial-cards-per-stack=$NUM" - The maximal number of initial
+cards per stack.
+
+Notice that it's very important to set the maximal number of initial cards
+per stack, or else it's possible that a stack will eventually overflow.
+
+"Compact" States
+---------------------
+
+In Compact States, the contents of the card stacks are stored inside the
+states, rather than in a central collection (where the states contain only
+pointers). Despite its name, it actually consume more memory than Indirect
+Stack States which is the default.
+
+Compact states used to be faster than Indirect Stack States, but now it
+seems indirect stack states is at least slightly faster even for games
+whose stacks are not very long. If you still would wish to enable it,
+run ./configure with the "--enable-states-type=compact" flag.
+
+Installing under Win32
+----------------------
+
+Freecell Solver is distributed with a makefile suitable for use with
+Microsoft Visual C++. To build it using it follow the following steps:
+
+1. Copy "config.h.win32" to "config.h" and "prefix.h.win32" to "prefix.h";
+In the directory Presets/ copy presetrc.win32 to presetrc.
+
+2. Optionally, edit it to set its preferences
+
+3. Type "nmake /f Makefile.win32".
+
+If you have an IDE of some sort you can take the following steps to compile
+Freecell Solver:
+
+1. Open a project for Freecell Solver.
+
+2. Add all the C files except "test_multi_parallel.c" to the project.
+
+3. Copy the file config.h.win32 to config.h and prefix.h.win32 to prefix.h.
+
+4. Build.
+
+If you are using gcc or some other command-line compiler, you should
+write the makefile based on the files "Makefile" or "Makefile.lite",
+and then compile according to it.
+
diff --git a/kpat/freecell-solver/Makefile.am b/kpat/freecell-solver/Makefile.am
new file mode 100644
index 00000000..b0f5acff
--- /dev/null
+++ b/kpat/freecell-solver/Makefile.am
@@ -0,0 +1,5 @@
+
+noinst_LTLIBRARIES = libfcs.la
+AM_CPPFLAGS = -DFCS_STATE_STORAGE=FCS_STATE_STORAGE_INTERNAL_HASH -DFCS_STACK_STORAGE=FCS_STACK_STORAGE_INTERNAL_HASH
+libfcs_la_SOURCES = alloc.c app_str.c caas.c card.c cl_chop.c cmd_line.c fcs_dm.c fcs_hash.c fcs_isa.c freecell.c intrface.c lib.c lookup2.c move.c pqueue.c preset.c rand.c scans.c simpsim.c state.c
+
diff --git a/kpat/freecell-solver/Makefile.lite b/kpat/freecell-solver/Makefile.lite
new file mode 100644
index 00000000..2a6248eb
--- /dev/null
+++ b/kpat/freecell-solver/Makefile.lite
@@ -0,0 +1,94 @@
+
+CC = gcc
+OFLAGS = -Wall -O3 -Wno-long-long -Wundef -Wcast-align -Wconversion -Wchar-subscripts -W -Wpointer-arith -Wwrite-strings -Wformat-security -Wmissing-format-attribute -fno-common -g
+OLFLAGS = -Wall
+DLFLAGS =
+
+END_OLFLAGS =
+END_DLFLAGS =
+
+INCLUDES = alloc.h app_str.h caas.h card.h cl_chop.h fcs_config.h fcs_cl.h fcs.h fcs_dm.h fcs_enums.h fcs_hash.h fcs_isa.h fcs_move.h fcs_user.h inline.h jhjtypes.h lookup2.h move.h ms_ca.h prefix.h pqueue.h preset.h rand.h state.h test_arr.h tests.h
+
+TARGETS = fc-solve
+
+all: $(TARGETS)
+
+board_gen: dummy
+ make -C board_gen/
+
+dummy:
+
+#<<<OBJECTS.START
+alloc.o: alloc.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+app_str.o: app_str.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+caas.o: caas.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+card.o: card.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+cl_chop.o: cl_chop.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+cmd_line.o: cmd_line.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+fcs_dm.o: fcs_dm.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+fcs_hash.o: fcs_hash.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+fcs_isa.o: fcs_isa.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+freecell.o: freecell.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+intrface.o: intrface.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+lib.o: lib.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+lookup2.o: lookup2.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+move.o: move.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+pqueue.o: pqueue.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+preset.o: preset.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+rand.o: rand.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+scans.o: scans.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+simpsim.o: simpsim.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+state.o: state.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+main.o: main.c $(INCLUDES)
+ $(CC) -c $(OFLAGS) -o $@ $<
+
+OBJECTS = alloc.o app_str.o caas.o card.o cl_chop.o cmd_line.o fcs_dm.o fcs_hash.o fcs_isa.o freecell.o intrface.o lib.o lookup2.o move.o pqueue.o preset.o rand.o scans.o simpsim.o state.o main.o
+#>>>OBJECTS.END
+
+fc-solve: $(OBJECTS)
+ $(CC) $(OLFLAGS) -o $@ $(OBJECTS) -lm
+
+clean:
+ rm -f *.o $(TARGETS) $(DTARGETS)
+
+
diff --git a/kpat/freecell-solver/README b/kpat/freecell-solver/README
new file mode 100644
index 00000000..4902e3a7
--- /dev/null
+++ b/kpat/freecell-solver/README
@@ -0,0 +1,105 @@
+1. Introduction
+---------------
+
+This is Freecell Solver version 2.8.x, a program that automatically
+solves most Freecell and Simple Simon games.
+
+Freecell Solver is distributed under the public domain.
+
+I hope you'll enjoy using it, and make the best of it.
+
+ Shlomi Fish (shlomif@vipe.technion.ac.il)
+
+2. Building
+-----------
+
+Read the file "INSTALL" for information on how to do that. For the impatient:
+type:
+
+./configure
+make
+make install
+
+3. Usage
+--------
+
+The program is called "fc-solve". You invoke it like this:
+
+fc-solve board_file
+
+board_file is the filename with a valid Freecell startup board. The file is
+built as follows:
+
+It has the 8 Freecell stacks.
+Each stack contain its number of cards separated by a whitespace
+and terminated with a newline character( it's important that the last stack
+will also be terminated with a newline !). The cards in the line are ordered
+from the bottom-most card in the left to the topmost card in the right.
+
+A card string contains the card number followed by the card deck. The card
+number is one of: A,1,2,3,4,5,6,7,8,9,10,J,Q,K. The card deck is one of:
+H,S,D,C (standing for Hearts, Spades, Diamonds and Clubs respectively).
+
+Here is an example board: (PySol board No. 24)
+
+4S 2S 9S 8S QC 4C 2H
+5H QH 3S AS 3H 4H QD
+QS 9C 6H 9H 3C KC 3D
+5D 2C JS 5S JH 6D AC
+2D KD 10H 10S 10D 8D
+7H JC KH 10C KS 7S
+AH 5C 6C AD 8H JD
+7C 6S 7D 4D 8C 9D
+
+And another one: (PySol board No. 198246790)
+
+KD JH 5H 7D 9H KS 9D
+3H JD 5D 8H QH 7H 2D
+4D 3C QS 3S 6C QC KC
+10S 9C 6D 9S QD 8C 10D
+10C 8S 7C 10H 2S AC
+8D AS AH 4H JS 4S
+6H 7S 4C 5C 5S JC
+AD KH 6S 2H 3D 2C
+
+You can specify the contents of the freecells by prefixing the line with
+"FC:". For example:
+FC: 3H QC
+
+will specify that the cards 3 of hearts and queen of clubs are present in
+the freecells. To specify an empty freecell use a "-" as its designator.
+
+If there's another "FC:" line, the previous line will be overriden.
+
+You can specify the contents of the foundations by prefixing the line with
+"Founds:" and then using a format as follows:
+
+Founds: H-5 C-A S-0 D-K
+
+Hence, the deck ID followed by a dash followed by the card number in the
+foundation. A suit that is not present will be assumed to be 0. Again, if
+there's more than one then the previous lines will be overriden.
+
+
+The program will stop processing the input as soon as it read 8 lines of
+standard stacks. Therefore, it is recommended that the foundations and
+freecells lines will come at the beginning of the file.
+
+The program will process the board and try to solve it. If it succeeds it
+will output the states from the initial board to its final solution to the
+standard output. If it fails, it will notify it.
+
+For information about the various command-line switches that Freecell
+Solver accepts, read the USAGE file in this directory.
+
+To solve Simple Simon boards append "--game simple_simon" right after
+the "fc-solve" program name.
+
+4. The board generation programs
+--------------------------------
+
+Several programs which can generate the initial boards of various Freecell
+implementations can be found in the "board_gen/" sub-directory. Read the
+"README" file there for details on how they can be compiled and used.
+
+In any case, they can save you the time of inputting the board yourself.
diff --git a/kpat/freecell-solver/USAGE b/kpat/freecell-solver/USAGE
new file mode 100644
index 00000000..f78295b2
--- /dev/null
+++ b/kpat/freecell-solver/USAGE
@@ -0,0 +1,518 @@
+Freecell Solver's Command-Line Syntax and Usage
+===============================================
+
+
+1. The programs
+---------------
+
+Most command-line switches have two versions: a short POSIX one which
+is a dash followed by a letter; and a long GNU one which is two dashes
+followed by the command string. Note, that Freecell Solver does not
+support specifying more than one command letter after a dash, (e.g:
+"-sip"). Furthermore, a command that accepts a parameter, will require
+this parameter to be present in the next command-line argument, not in
+the GNU manner of "--command=option".
+
+I don't use getopt because I want Freecell Solver to be a pure ANSI C
+program, so I'm sorry for the inconvenience.
+
+
+2. Getting Help
+---------------
+
+-h --help
+
+This option displays a help text on the screen. This help
+text summarizes the command-line options and their meaning, as well as
+the signal combinations that fc-solve accepts.
+
+
+3. Output Options
+-----------------
+
+-p --parseable-output
+
+This option will display the stacks in a format that can be more easily
+manipulated by text-processing programs such as grep or perl. Namely,
+The freecells will be displayed in one line, and the foundations in a
+separate line. Plus, Each stack will be displayed horizontally, in its
+own line, while beginning with a ":".
+
+
+-t --display-10-as-t
+
+This option will display the 10 cards as a capital T instead of a 10.
+Thus, the cards will be more properly aligned.
+
+
+-c --canonized-order-output
+
+Freecell Solver re-arranges the stacks and freecells in a given state
+according to their first card. It keeps their actual position in a
+separate place, but internally it uses their canonized place. Use
+this option, if you want Freecell Solver to display them in that order.
+One should be warned that that way the place of a given stack in the
+board will not be preserved throughout the solution.
+
+
+-m --display-moves
+
+This option will display the moves instead of the intermediate states.
+Each move will be displayed in a separate line, in a format that is
+human-readable, but that can also be parsed and analyzed by a computer
+program with some effort on the programmer's part.
+
+
+-sn --standard-notation
+
+This option will display the moves in standard notation in which every
+move consists of two characters and there are ten moves in a line. Naturally,
+this option will only become apparent if the display moves is specified.
+(it does not implicitly specify it, though).
+
+For more information regarding standard notation refer to the following
+web-page:
+
+http://home.earthlink.net/~fomalhaut/freecell.html
+
+-snx --standard-notation-extended
+
+This option is similar to the previous one, only that when a sequence
+move is made to an empty stack with more than one card in the sequence,
+the move will be followed with "v" and the number of cards moved in
+hexadecimal.
+
+-sam --display-states-and-moves
+
+This option will display both the intermediate states and the moves that
+are needed to move from one to another. The standard notation
+option applies to it to.
+
+
+-pi --display-parent-iter
+
+This option (assuming the -s and -i options are specified) will also
+display the iteration index of the state from which the current state
+was derived. This is especially useful for A* or BFS scans.
+
+4. Game Variants Options
+------------------------
+
+
+--freecells-num [Number of Freecells]
+
+This option specifies the number of freecells which are available to
+the program. Freecell Solver can use any number of freecells as long as
+it does not exceed its maximal number.
+
+This maximum is hard-coded into the program, and can be specified at
+compile-time by modifying the file "config.h". See the file INSTALL for
+details.
+
+
+--stacks-num [Number of Stacks]
+
+This option specifies the number of stacks present in the board. Again,
+this number cannot exceed the maximal number of stacks, which can be
+specified in the file "config.h" during compile-time of Freecell
+Solver.
+
+
+--decks-num [Number of Decks]
+
+This options specifies how many decks are found in the board. This number
+cannot exceed the maximal number of decks, which can be specified in the
+file "config.h" during compile time of Freecell Solver.
+
+
+--sequences-are-built-by {suit|alternate_color|rank}
+
+This option specifies whether a card sequence is built by suit or by
+alternate colour or by rank regardless of suit.
+
+
+--sequence-move {limited|unlimited}
+
+This option specifies whether the sequence move is limited by the
+number of freecells or vacant stacks or not.
+
+
+--empty-stacks-filled-by {kings|none|all}
+
+Specifies which cards can fill an empty stack.
+
+
+--game [game]
+--preset [game]
+-g [game]
+
+Specifies the type of game. Each preset implies several of the
+settings options above and sometimes even the tests order below.
+Available presets:
+
+ bakers_dozen - Baker's Dozen
+ bakers_game - Baker's Game
+ beleaguered_castle - Beleaguered Castle
+ citadel - Citadel
+ cruel - Cruel
+ der_katz - Der Katzenschwanz
+ die_schlange - Die Schlange
+ eight_off - Eight Off
+ fan - Fan
+ forecell - Forecell
+ freecell - Freecell
+ good_measure - Good Measure
+ ko_bakers_game - Kings' Only Baker's Game
+ relaxed_freecell - Relaxed Freecell
+ relaxed_sehaven - Relaxed Seahaven Towers
+ seahaven - Seahaven Towers
+ simple_simon - Simple Simon
+ streets_and_alleys - Streets and Alleys
+
+Note: in order to solve Der Katzenschwanz and Die Schlange I recommend you
+compile Freecell Solver with the INDIRECT_STACK_STATES option, or else it will
+consume much more memory. For details consult the file INSTALL.
+
+5. Solving Algorithm Options
+----------------------------
+
+-mi [Maximal number of iterations]
+--max-iters [Maximal number of iterations]
+
+This parameter limits the maximal number of states to check. This will
+give a rough estimate on the time spent to solve a given board.
+
+
+-md [Maximal depth]
+--max-depth [Maximal depth]
+
+Freecell Solver recurses into the solution. This parameter specifies a
+maximal recursion depth. Generally speaking, it's not a good idea to
+set it, because that way several important intermediate states become
+inaccessible.
+
+-mss [Maximal States' Number]
+--max-stored-states [Maximal States' Number]
+
+Limits the number of the states stored by the program in the computer's
+memory. This differs from the maximal number of iterations in the sense, that
+it is possible that a stored state was not checked yet.
+
+
+-to [Test's Order]
+--tests-order [Test's Order]
+
+This option specifies the order in which Freecell Solver will try the
+different types of moves that it can perform. Each move is specified by
+one character, and they are performed in the order in which they appear
+in the parameter string. You can omit tests by not including their
+corresponding characters in the string.
+
+The tests along with their characters are:
+
+Freecell Tests:
+
+'0' - put top stack cards in the foundations.
+'1' - put freecell cards in the foundations.
+'2' - put freecell cards on top of stacks.
+'3' - put non-top stack cards in the foundations.
+'4' - move stack cards to different stacks.
+'5' - move stack cards to a parent card on the same stack.
+'6' - move sequences of cards onto free stacks.
+'7' - put freecell cards on empty stacks.
+'8' - move cards to a different parent.
+'9' - empty an entire stack into the freecells.
+
+Atomic Freecell Tests:
+
+'A' - move a stack card to an empty stack.
+'B' - move a stack card to a parent on a different stack.
+'C' - move a stack card to a freecell.
+'D' - move a freecell card to a parent.
+'E' - move a freecell card to an empty stack.
+
+
+Simple Simon Tests:
+
+'a' - move a full sequence to the foundations.
+'b' - move a sequence to a true parent of his.
+'c' - move a whole stack sequence to a false parent (in order to clear
+ the stack)
+'d' - move a sequence to a true parent that has some cards above it.
+'e' - move a sequence with some cards above it to a true parent.
+'f' - move a sequence with a junk sequence above it to a true parent that
+ has some cards above it.
+'g' - move a whole stack sequence to a false parent which has some
+ cards above it.
+'h' - move a sequence to a parent on the same stack.
+
+Manipulating the tests order can be very helpful to the quick solution
+of a given board. If you found that a certain board cannot be solved in
+after a long time or in a certain maximal number of iterations, you
+should try different tests' orders. Usually, one can find a test order
+that solves a board very quickly.
+
+Note that this test order usually makes sense only for the Depth-First
+Search scans (see the "--method" option below).
+
+Also note that Freecell tests are not suitable for solving Simple Simon games
+and Simple Simon tests are not suitable for solving anything except Simple
+Simon.
+
+Tests can be grouped together into random groups using parenthesis
+(e.g: "(0123)") or square brackets ("[012][3456789]"). Such grouping is
+only relevant to the Random DFS scan (see below).
+
+
+-me [Solving Method]
+--method [Solving Method]
+
+This option specifies the solving method that will be used to solve the
+board. Currently, the following methods are available:
+
+a-star - An A* scan
+bfs - A Breadth-First Search (or BFS) scan
+dfs - A Depth-First Search (or DFS) scan
+random-dfs - A randomized DFS scan
+soft-dfs - A "soft" DFS scan
+
+The difference between "dfs" and "soft-dfs" is that the soft DFS does not
+use procedural recursion but rather its own internal stack. "random-dfs" is
+similar to "soft-dfs" only it determines to which states to recurse into
+randomly. Its behaviour will differ depending on the seed you supply to it.
+(see the "-seed" option below.)
+
+BFS does not yield good results, and A* has a mixed behaviour, so for
+the time being I recommend using either DFS or Soft-DFS.
+
+The Random-DFS scan processes every tests' random group, randomizes the
+states that it found and recurses into them one by one. Renegade tests
+that do not belong to any group, are processed in a non-random manner.
+
+
+-asw [A* Weights]
+--a-star-weight [A* Weights]
+
+Specify weights for the A* scan, assuming it is used. The parameter
+should be a comma-separated list of numbers, each one is proportional
+to the weight of its corresponding test.
+
+The numbers are, in order:
+1. The number of cards out.
+2. The maximal sequence move.
+3. The number of cards under sequences.
+4. The length of the sequences which are found over renegade cards.
+5. The depth of the board in the solution.
+
+The default weights are respectively: 0.5,0,0.3,0,0.2
+
+
+-seed [Seed Number]
+
+Specifies a seed to be used by Freecell Solver's internal random number
+generator. This seed may alter the behaviour and speed of the "random-dfs"
+scan.
+
+
+-opt
+--optimize-solution
+
+This option instructs Freecell Solver to try and optimize the solution
+path so it will have a smaller number of moves.
+
+
+-opt-to [tests order]
+--optimization-tests-order [tests order]
+
+This argument specifies the test order for the optimization scan, in case
+it should be different than an order that contains all the tests that were
+used in all the normal scans.
+
+
+--reparent-states
+
+This option specifies that states that were encountered whose depth in the
+states graph can be improved should be reparented to the new parent. This
+option can possibly make solutions shorter.
+
+
+--calc-real-depth
+
+This options become effective only if --reparent-states is specified. What it
+does, is explicitly calculate the depth of the state by tracing its path
+to the initial state. This may make depth consideration more accurate.
+
+
+6. Running Several Scans in Parallel:
+-------------------------------------
+
+Starting from Version 2.4.0, Freecell Solver can run several scans in
+parallel on the same state collection. Each scan resides in its own
+"Soft Thread". By specifying several soft threads on the command line
+one can create use several parallel scans. Once one of the scans
+reaches a solution, the solution will be displayed.
+
+
+-nst
+--next-soft-thread
+
+This option creates a new soft-thread and let the other scan-specific options
+initialize it. For example:
+
+# fc-solve --method a-star -nst --method soft-dfs -to 0123467 myboard.txt
+
+will run an A* scan and a Soft-DFS scan with a tests order of 0123467 on
+myboard.txt.
+
+
+-step [Number of Iterations in the Step]
+--soft-thread-step [Number of Iterations in the Step]
+
+This option will set the number of iterations with which to run the
+soft thread before switching to the next one. By specifying a larger
+step, one can give a certain scan a longer run-time and a higher priority.
+
+
+-nht
+--next-hard-thread
+
+This argument lets one initialize the next hard thread. If Freecell Solver was
+compiled with such support, then it is possible to run each hard thread in its
+own system thread. Each hard-thread contains one or more soft threads.
+
+
+--st-name [soft thread name]
+
+This argument sets the name used to identify the current soft thread. This name
+can later be used to construct the prelude (see below).
+
+
+--prelude [i1@st1{,i2@st2{,i3@st3...}}]
+
+Sets the prelude for the hard thread. At the beginning of the search, the
+hard thread plays a static sequence of iterations at each of the soft threads
+specified in the prelude, for the number of iterations specified.
+
+For example, if you had three soft threads named "foo", "bar" and "rin", then
+the following prelude:
+
+ --prelude 500@foo,1590@bar,100@foo,200@rin
+
+Will run 500 iterations in "foo", then 1590 in "bar", then 100 in "foo" again,
+and then 200 in "rin". After the prelude finishes, the hard thread would
+run the scans one after the other in the sequence they were defined for their
+step number.
+
+
+--scans-synergy {none|dead-ends-mark}
+
+Specifies the synergy between the various scans, or how much they cooperate
+between themselves. "none" means they do not cooperate and only share
+the same memory resources. "dead-end-marks" means they try to mark states
+that they have withdrawn from, and states whose all their derived states are
+such, as "dead ends". This may or may not improve the speed of the solution.
+
+
+-ni
+--next-instance
+
+This option allows to run two or more separate solvers one after the
+other. If the first one returned an unsolvable verdict, then the second
+one would run and so on. One use of it is to run an atomic moves scan
+after a meta-moves scan, so we will always get an accurate verdict and
+still enjoy some of the speed of the meta-moves scan.
+
+
+7. Meta-Options
+---------------
+
+
+--reset
+
+This option resets the program to its initial state, losing all the
+logic that was inputted to it up to that state. Afterwards, it can
+be set to a different configuration, again.
+
+
+--read-from-file [{num_skip},]filename
+
+This option will read the configuration options from a file. The format
+of the file is similar to that used by the UNIX Bourne Shell. (i.e:
+spaces denote separate arguments, double-quotes encompass arguments,
+backslash escapes characters).
+
+The filename can be preceeded by an optional number of the arguments to
+skip followed by a comma. (the default is 0)
+
+
+-l [preset]
+--load-config [preset]
+
+Reads the configuration specified by [preset] and configures the solver
+accordingly. A preset is a set of command line arguments to be analyzed
+in the place of this option. They are read from a set of presetrc files
+: one installed system-wide, the other at $HOME/.freecell-solver/presetrc
+and the third at the path specified by the FREECELL_SOLVER_PRESETRC
+environment variable. You can add more presets at any of these places.
+(refer to http://groups.yahoo.com/group/fc-solve-discuss/message/403
+for information about their format)
+
+Presets that are shipped with Freecell Solver:
+
+ abra-kadabra - a meta-moves preset
+ cool-jives - a meta-moves preset
+ crooked-nose - an atomic-moves preset (guarantees an accurate verdict)
+ fools-gold - an atomic-moves preset
+ good-intentions - runs cool-jives and then fools-gold
+ hello-world - a meta-moves preset
+ john-galt-line - a meta-moves preset
+ rin-tin-tin - a meta-moves preset
+ yellow-brick-road - a meta-moves preset
+
+They can be abbreviated into their lowercase acronym (i.e: "ak" or "rtt").
+
+
+8. Run-time Display Options
+---------------------------
+
+
+-i
+--iter-output
+
+This option tells fc-solve to print the iteration number and the
+recursion depth of every state which is checked, to the standard
+output. It's a good way to keep track of how it's doing, but the output
+slows it down a bit.
+
+
+-s
+--state-output
+
+This option implies -i. If specified, this option outputs the cards and
+formation of the board itself, for every state that is checked.
+"fc-solve -s" yields a nice real-time display of the progress of
+Freecell Solver, but you usually cannot make what is going on because
+it is so fast.
+
+
+9. Signal Combinations
+----------------------
+
+If you are working on a UNIX or a similar system then you can set some
+run-time options in "fc-solve" by sending it some signal
+combinations.
+
+If you send the signal USR1, without sending any other signals before
+that, then "fc-solve" will output the present number of
+iterations. This method is a good way to monitor an instance that takes
+a long time to solve.
+
+If you send it the signal USR2 and then USR1, then "fc-solve"
+will print the iteration number and depth on every state that it
+checks. It is the equivalent of specifying (or unspecifying) the
+option -i/--iter-output.
+
+If you send it two USR2 signals and then USR1, then "fc-solve"
+will also print the board of every state. Again, this will only be done
+assuming the iteration output is turned on.
+
diff --git a/kpat/freecell-solver/alloc.c b/kpat/freecell-solver/alloc.c
new file mode 100644
index 00000000..81abdcc5
--- /dev/null
+++ b/kpat/freecell-solver/alloc.c
@@ -0,0 +1,127 @@
+/*
+ * alloc.c - a dynamic memory allocator. It allocates blocks of relatively
+ * small size, in a contiguous, compact manner. The most recent block can
+ * be released, but otherwise the blocks are kept for prosperity.
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2002
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "fcs_config.h"
+
+#include "alloc.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+#define ALLOCED_SIZE (8*1024-10*sizeof(char *))
+
+fcs_compact_allocator_t *
+ freecell_solver_compact_allocator_new(void)
+{
+ fcs_compact_allocator_t * allocator;
+
+
+ allocator = (fcs_compact_allocator_t *)malloc(sizeof(*allocator));
+ allocator->max_num_packs = IA_STATE_PACKS_GROW_BY;
+ allocator->packs = (char * *)malloc(sizeof(allocator->packs[0]) * allocator->max_num_packs);
+ allocator->num_packs = 1;
+ allocator->max_ptr =
+ (allocator->ptr =
+ allocator->rollback_ptr =
+ allocator->packs[0] =
+ malloc(ALLOCED_SIZE))
+ + ALLOCED_SIZE;
+
+ return allocator;
+}
+
+void freecell_solver_compact_allocator_extend(
+ fcs_compact_allocator_t * allocator
+ )
+{
+ /* Allocate a new pack */
+ if (allocator->num_packs == allocator->max_num_packs)
+ {
+ allocator->max_num_packs += IA_STATE_PACKS_GROW_BY;
+ allocator->packs = (char * *)realloc(allocator->packs, sizeof(allocator->packs[0]) * allocator->max_num_packs);
+ }
+
+ allocator->max_ptr =
+ (allocator->ptr =
+ allocator->rollback_ptr =
+ allocator->packs[allocator->num_packs++] =
+ malloc(ALLOCED_SIZE))
+ + ALLOCED_SIZE;
+}
+
+#if 0
+char *
+ freecell_solver_compact_allocator_alloc(
+ fcs_compact_allocator_t * allocator,
+ int how_much
+ )
+{
+ if (allocator->max_ptr - allocator->ptr < how_much)
+ {
+ freecell_solver_compact_allocator_extend(allocator);
+ }
+ allocator->rollback_ptr = allocator->ptr;
+ allocator->ptr += (how_much+(4-(how_much&0x3)));
+ return allocator->rollback_ptr;
+}
+
+void freecell_solver_compact_allocator_release(fcs_compact_allocator_t * allocator)
+{
+ allocator->ptr = allocator->rollback_ptr;
+}
+#endif
+
+void freecell_solver_compact_allocator_finish(fcs_compact_allocator_t * allocator)
+{
+ int a;
+ for(a=0;a<allocator->num_packs;a++)
+ {
+ free(allocator->packs[a]);
+ }
+ free(allocator->packs);
+ free(allocator);
+}
+
+void freecell_solver_compact_allocator_foreach(
+ fcs_compact_allocator_t * allocator,
+ int data_width,
+ void (*ptr_function)(void *, void *),
+ void * context
+ )
+{
+ int pack;
+ char * ptr, * max_ptr;
+ for(pack=0;pack<allocator->num_packs-1;pack++)
+ {
+ ptr = allocator->packs[pack];
+ max_ptr = ptr + ALLOCED_SIZE - data_width;
+ while (ptr <= max_ptr)
+ {
+ ptr_function(ptr, context);
+ ptr += data_width;
+ }
+ }
+ /* Run the callback on the last pack */
+ ptr = allocator->packs[pack];
+ max_ptr = allocator->rollback_ptr;
+ while (ptr <= max_ptr)
+ {
+ ptr_function(ptr, context);
+ ptr += data_width;
+ }
+}
+
+
+
+
diff --git a/kpat/freecell-solver/alloc.h b/kpat/freecell-solver/alloc.h
new file mode 100644
index 00000000..5b339f24
--- /dev/null
+++ b/kpat/freecell-solver/alloc.h
@@ -0,0 +1,86 @@
+
+#ifndef FC_SOLVE__ALLOC_H
+#define FC_SOLVE__ALLOC_H
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+struct fcs_compact_allocator_struct
+{
+ char * * packs;
+ int max_num_packs;
+ int num_packs;
+ char * max_ptr;
+ char * ptr;
+ char * rollback_ptr;
+};
+
+typedef struct fcs_compact_allocator_struct fcs_compact_allocator_t;
+
+extern fcs_compact_allocator_t *
+ freecell_solver_compact_allocator_new(void);
+
+extern void freecell_solver_compact_allocator_extend(
+ fcs_compact_allocator_t * allocator
+ );
+#if 0
+extern char *
+ freecell_solver_compact_allocator_alloc(
+ fcs_compact_allocator_t * allocator,
+ int how_much
+ );
+#else
+#define fcs_compact_alloc_into_var(result,allocator_orig,what_t) \
+{ \
+ register fcs_compact_allocator_t * allocator = (allocator_orig); \
+ if (allocator->max_ptr - allocator->ptr < sizeof(what_t)) \
+ { \
+ freecell_solver_compact_allocator_extend(allocator); \
+ } \
+ allocator->rollback_ptr = allocator->ptr; \
+ allocator->ptr += ((sizeof(what_t))+(sizeof(char *)-((sizeof(what_t))&(sizeof(char *)-1)))); \
+ result = (what_t *)allocator->rollback_ptr; \
+}
+
+#define fcs_compact_alloc_typed_ptr_into_var(result, type_t, allocator_orig, how_much_orig) \
+{ \
+ register fcs_compact_allocator_t * allocator = (allocator_orig); \
+ register int how_much = (how_much_orig); \
+ if (allocator->max_ptr - allocator->ptr < how_much) \
+ { \
+ freecell_solver_compact_allocator_extend(allocator); \
+ } \
+ allocator->rollback_ptr = allocator->ptr; \
+ /* Round ptr to the next pointer boundary */ \
+ allocator->ptr += ((how_much)+((sizeof(char *)-((how_much)&(sizeof(char *)-1)))&(sizeof(char*)-1))); \
+ result = (type_t *)allocator->rollback_ptr; \
+}
+
+#endif
+
+#if 0
+extern void freecell_solver_compact_allocator_release(fcs_compact_allocator_t * allocator);
+#else
+#define fcs_compact_alloc_release(allocator) \
+{ \
+ (allocator)->ptr = (allocator)->rollback_ptr; \
+}
+#endif
+
+extern void freecell_solver_compact_allocator_finish(fcs_compact_allocator_t * allocator);
+
+extern void freecell_solver_compact_allocator_foreach(
+ fcs_compact_allocator_t * allocator,
+ int data_width,
+ void (*ptr_function)(void *, void *),
+ void * context
+ );
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
diff --git a/kpat/freecell-solver/app_str.c b/kpat/freecell-solver/app_str.c
new file mode 100644
index 00000000..0a1ced21
--- /dev/null
+++ b/kpat/freecell-solver/app_str.c
@@ -0,0 +1,74 @@
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#define GROW_BY 4000
+
+struct freecell_solver_append_string_struct
+{
+ char * buffer;
+ char * end_of_buffer;
+ size_t max_size;
+ size_t size_of_margin;
+};
+
+typedef struct freecell_solver_append_string_struct freecell_solver_append_string_t;
+
+freecell_solver_append_string_t * freecell_solver_append_string_alloc(int size_margin)
+{
+ freecell_solver_append_string_t * app_str;
+
+ if (size_margin > GROW_BY)
+ {
+ return NULL;
+ }
+
+ app_str = malloc(sizeof(freecell_solver_append_string_t));
+ app_str->max_size = GROW_BY;
+ app_str->end_of_buffer = app_str->buffer = malloc(app_str->max_size);
+ app_str->size_of_margin = size_margin;
+
+ return app_str;
+}
+
+int freecell_solver_append_string_sprintf(
+ freecell_solver_append_string_t * app_str,
+ char * format,
+ ...
+ )
+{
+ int num_chars_written;
+ va_list my_va_list;
+
+ va_start(my_va_list, format);
+ num_chars_written = vsprintf(app_str->end_of_buffer, format, my_va_list);
+ app_str->end_of_buffer += num_chars_written;
+ /*
+ * Check to see if we don't have enough space in which case we should
+ * resize
+ * */
+ if (app_str->buffer + app_str->max_size - app_str->end_of_buffer < (int)app_str->size_of_margin )
+ {
+ char * old_buffer = app_str->buffer;
+ app_str->max_size += GROW_BY;
+ app_str->buffer = realloc(app_str->buffer, app_str->max_size);
+ /*
+ * Adjust end_of_buffer to the new buffer start
+ * */
+ app_str->end_of_buffer = app_str->buffer + (app_str->end_of_buffer - old_buffer);
+ }
+
+ return num_chars_written;
+}
+
+char * freecell_solver_append_string_finalize(
+ freecell_solver_append_string_t * app_str
+ )
+{
+ char * ret;
+ ret = strdup(app_str->buffer);
+ free(app_str->buffer);
+ free(app_str);
+ return ret;
+}
diff --git a/kpat/freecell-solver/app_str.h b/kpat/freecell-solver/app_str.h
new file mode 100644
index 00000000..c6b6732a
--- /dev/null
+++ b/kpat/freecell-solver/app_str.h
@@ -0,0 +1,39 @@
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#ifndef FC_SOLVE__APP_STR_H
+#define FC_SOLVE__APP_STR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct freecell_solver_append_string_struct
+{
+ char * buffer;
+ char * end_of_buffer;
+ int max_size;
+ int size_of_margin;
+};
+
+typedef struct freecell_solver_append_string_struct freecell_solver_append_string_t;
+
+extern freecell_solver_append_string_t * freecell_solver_append_string_alloc(int size_margin);
+
+extern int freecell_solver_append_string_sprintf(
+ freecell_solver_append_string_t * app_str,
+ const char * format,
+ ...
+ );
+
+extern char * freecell_solver_append_string_finalize(
+ freecell_solver_append_string_t * app_str
+ );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef FC_SOLVE__APP_STR_H */
diff --git a/kpat/freecell-solver/caas.c b/kpat/freecell-solver/caas.c
new file mode 100644
index 00000000..82492f34
--- /dev/null
+++ b/kpat/freecell-solver/caas.c
@@ -0,0 +1,629 @@
+/*
+ * caas.c - the various possible implementations of the function
+ * freecell_solver_check_and_add_state().
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+
+#ifndef FC_SOLVE__CAAS_C
+#define FC_SOLVE__CAAS_C
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "fcs_dm.h"
+#include "fcs.h"
+
+#include "fcs_isa.h"
+
+#include "lookup2.h"
+
+
+#ifdef INDIRECT_STACK_STATES
+#include "fcs_hash.h"
+#endif
+
+#include "caas.h"
+#include "ms_ca.h"
+
+#include "test_arr.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+
+/*
+ The objective of the fcs_caas_check_and_insert macros is:
+ 1. To check if new_state is already in the prev_states collection.
+ 2. If not, to add it and to set check to true.
+ 3. If so, to set check to false.
+ */
+
+
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH)
+#ifdef FCS_WITH_MHASH
+#define fcs_caas_check_and_insert() \
+ /* \
+ Calculate the has function of the state. \
+ */ \
+ { \
+ char * temp_ptr; \
+ instance->mhash_context = mhash_init(instance->mhash_type); \
+ mhash(instance->mhash_context, (void *)new_state, sizeof(fcs_state_t)); \
+ temp_ptr = mhash_end(instance->mhash_context); \
+ /* Retrieve the first 32 bits and make them the hash value */ \
+ hash_value_int = *(SFO_hash_value_t*)temp_ptr; \
+ free(temp_ptr); \
+ } \
+ \
+ if (hash_value_int < 0) \
+ { \
+ /* \
+ * This is a bit mask that nullifies the sign bit of the \
+ * number so it will always be positive \
+ * */ \
+ hash_value_int &= (~(1<<((sizeof(hash_value_int)<<3)-1))); \
+ } \
+ check = ((*existing_state = freecell_solver_hash_insert( \
+ instance->hash, \
+ new_state, \
+ hash_value_int, \
+ 1 \
+ )) == NULL);
+
+
+
+#else
+#define fcs_caas_check_and_insert() \
+ { \
+ const char * s_ptr = (char*)new_state; \
+ const char * s_end = s_ptr+sizeof(fcs_state_t); \
+ hash_value_int = 0; \
+ while (s_ptr < s_end) \
+ { \
+ hash_value_int += (hash_value_int << 5) + *(s_ptr++); \
+ } \
+ hash_value_int += (hash_value_int>>5); \
+ } \
+ if (hash_value_int < 0) \
+ { \
+ /* \
+ * This is a bit mask that nullifies the sign bit of the \
+ * number so it will always be positive \
+ * */ \
+ hash_value_int &= (~(1<<((sizeof(hash_value_int)<<3)-1))); \
+ } \
+ check = ((*existing_state = freecell_solver_hash_insert( \
+ instance->hash, \
+ new_state, \
+ freecell_solver_lookup2_hash_function( \
+ (ub1 *)new_state, \
+ sizeof(fcs_state_t), \
+ 24 \
+ ), \
+ hash_value_int, \
+ 1 \
+ )) == NULL);
+
+#endif
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT)
+#define fcs_caas_check_and_insert() \
+ /* Try to see if the state is found in indirect_prev_states */ \
+ if ((pos_ptr = (fcs_state_with_locations_t * *)bsearch(&new_state, \
+ instance->indirect_prev_states, \
+ instance->num_indirect_prev_states, \
+ sizeof(fcs_state_with_locations_t *), \
+ freecell_solver_state_compare_indirect)) == NULL) \
+ { \
+ /* It isn't in prev_states, but maybe it's in the sort margin */ \
+ pos_ptr = (fcs_state_with_locations_t * *)freecell_solver_bsearch( \
+ &new_state, \
+ instance->indirect_prev_states_margin, \
+ instance->num_prev_states_margin, \
+ sizeof(fcs_state_with_locations_t *), \
+ freecell_solver_state_compare_indirect_with_context, \
+ NULL, \
+ &found); \
+ \
+ if (found) \
+ { \
+ check = 0; \
+ *existing_state = *pos_ptr; \
+ } \
+ else \
+ { \
+ /* Insert the state into its corresponding place in the sort \
+ * margin */ \
+ memmove((void*)(pos_ptr+1), \
+ (void*)pos_ptr, \
+ sizeof(fcs_state_with_locations_t *) * \
+ (instance->num_prev_states_margin- \
+ (pos_ptr-instance->indirect_prev_states_margin) \
+ )); \
+ *pos_ptr = new_state; \
+ \
+ instance->num_prev_states_margin++; \
+ \
+ if (instance->num_prev_states_margin >= PREV_STATES_SORT_MARGIN) \
+ { \
+ /* The sort margin is full, let's combine it with the main array */ \
+ if (instance->num_indirect_prev_states + instance->num_prev_states_margin > instance->max_num_indirect_prev_states) \
+ { \
+ while (instance->num_indirect_prev_states + instance->num_prev_states_margin > instance->max_num_indirect_prev_states) \
+ { \
+ instance->max_num_indirect_prev_states += PREV_STATES_GROW_BY; \
+ } \
+ instance->indirect_prev_states = realloc(instance->indirect_prev_states, sizeof(fcs_state_with_locations_t *) * instance->max_num_indirect_prev_states); \
+ } \
+ \
+ freecell_solver_merge_large_and_small_sorted_arrays( \
+ instance->indirect_prev_states, \
+ instance->num_indirect_prev_states, \
+ instance->indirect_prev_states_margin, \
+ instance->num_prev_states_margin, \
+ sizeof(fcs_state_with_locations_t *), \
+ freecell_solver_state_compare_indirect_with_context, \
+ NULL \
+ ); \
+ \
+ instance->num_indirect_prev_states += instance->num_prev_states_margin; \
+ \
+ instance->num_prev_states_margin=0; \
+ } \
+ check = 1; \
+ } \
+ \
+ } \
+ else \
+ { \
+ *existing_state = *pos_ptr; \
+ check = 0; \
+ }
+
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE)
+
+#define fcs_caas_check_and_insert() \
+ *existing_state = (fcs_state_with_locations_t *)rbsearch(new_state, instance->tree); \
+ check = ((*existing_state) == new_state);
+
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) || (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE)
+
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE)
+#define fcs_libavl_states_tree_insert(a,b) avl_insert((a),(b))
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE)
+#define fcs_libavl_states_tree_insert(a,b) rb_insert((a),(b))
+#endif
+
+#define fcs_caas_check_and_insert() \
+ *existing_state = fcs_libavl_states_tree_insert(instance->tree, new_state); \
+ check = (*existing_state == NULL);
+
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_TREE)
+#define fcs_caas_check_and_insert() \
+ *existing_state = g_tree_lookup(instance->tree, (gpointer)new_state); \
+ if (*existing_state == NULL) \
+ { \
+ /* The new state was not found. Let's insert it. \
+ * The value must be the same as the key, so g_tree_lookup() \
+ * will return it. */ \
+ g_tree_insert( \
+ instance->tree, \
+ (gpointer)new_state, \
+ (gpointer)new_state \
+ ); \
+ check = 1; \
+ } \
+ else \
+ { \
+ check = 0; \
+ }
+
+
+
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH)
+#define fcs_caas_check_and_insert() \
+ *existing_state = g_hash_table_lookup(instance->hash, (gpointer)new_state); \
+ if (*existing_state == NULL) \
+ { \
+ /* The new state was not found. Let's insert it. \
+ * The value must be the same as the key, so g_tree_lookup() \
+ * will return it. */ \
+ g_hash_table_insert( \
+ instance->hash, \
+ (gpointer)new_state, \
+ (gpointer)new_state \
+ \
+ ); \
+ check = 1; \
+ } \
+ else \
+ { \
+ check = 0; \
+ }
+
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_DB_FILE)
+#define fcs_caas_check_and_insert() \
+ { \
+ DBT key, value; \
+ key.data = new_state; \
+ key.size = sizeof(*new_state); \
+ if (instance->db->get( \
+ instance->db, \
+ NULL, \
+ &key, \
+ &value, \
+ 0 \
+ ) == 0) \
+ { \
+ /* The new state was not found. Let's insert it. \
+ * The value must be the same as the key, so g_tree_lookup() \
+ * will return it. */ \
+ \
+ value.data = key.data; \
+ value.size = key.size; \
+ instance->db->put( \
+ instance->db, \
+ NULL, \
+ &key, \
+ &value, \
+ 0); \
+ check = 1; \
+ } \
+ else \
+ { \
+ check = 0; \
+ *existing_state = (fcs_state_with_locations_t *)(value.data); \
+ } \
+ }
+
+#else
+#error no define
+#endif
+
+#ifdef INDIRECT_STACK_STATES
+static GCC_INLINE void freecell_solver_cache_stacks(
+ freecell_solver_hard_thread_t * hard_thread,
+ fcs_state_with_locations_t * new_state
+ )
+{
+ int a;
+#if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH)
+ SFO_hash_value_t hash_value_int;
+#endif
+ void * cached_stack;
+ fcs_card_t * new_ptr;
+ freecell_solver_instance_t * instance = hard_thread->instance;
+ int stacks_num = instance->stacks_num;
+
+
+ for(a=0 ; a<stacks_num ; a++)
+ {
+ /*
+ * If the stack is not a copy - it is already cached so skip
+ * to the next stack
+ * */
+ if (! (new_state->stacks_copy_on_write_flags & (1 << a)))
+ {
+ continue;
+ }
+ /* new_state->s.stacks[a] = realloc(new_state->s.stacks[a], fcs_stack_len(new_state->s, a)+1); */
+ fcs_compact_alloc_typed_ptr_into_var(new_ptr, char, hard_thread->stacks_allocator, (fcs_stack_len(new_state->s, a)+1));
+ memcpy(new_ptr, new_state->s.stacks[a], (fcs_stack_len(new_state->s, a)+1));
+ new_state->s.stacks[a] = new_ptr;
+
+#if FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH
+ /* Calculate the hash value for the stack */
+ /* This hash function was ripped from the Perl source code.
+ * (It is not derived work however). */
+ {
+ const char * s_ptr = (char*)(new_state->s.stacks[a]);
+ const char * s_end = s_ptr+fcs_stack_len(new_state->s, a)+1;
+ hash_value_int = 0;
+ while (s_ptr < s_end)
+ {
+ hash_value_int += (hash_value_int << 5) + *(s_ptr++);
+ }
+ hash_value_int += (hash_value_int >> 5);
+ }
+
+ if (hash_value_int < 0)
+ {
+ /*
+ * This is a bit mask that nullifies the sign bit of the
+ * number so it will always be positive
+ * */
+ hash_value_int &= (~(1<<((sizeof(hash_value_int)<<3)-1)));
+ }
+
+ cached_stack = (void *)freecell_solver_hash_insert(
+ instance->stacks_hash,
+ new_state->s.stacks[a],
+ (SFO_hash_value_t)freecell_solver_lookup2_hash_function(
+ (ub1 *)new_state->s.stacks[a],
+ (fcs_stack_len(new_state->s, a)+1),
+ 24
+ ),
+ hash_value_int,
+ 1
+ );
+
+#define replace_with_cached(condition_expr) \
+ if (cached_stack != NULL) \
+ { \
+ fcs_compact_alloc_release(hard_thread->stacks_allocator); \
+ new_state->s.stacks[a] = cached_stack; \
+ }
+
+ replace_with_cached(cached_stack != NULL);
+
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE) || (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE)
+ cached_stack =
+#if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE)
+ avl_insert(
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE)
+ rb_insert(
+#endif
+ instance->stacks_tree,
+ new_state->s.stacks[a]
+ );
+#if 0
+ ) /* In order to settle gvim and other editors that
+ are keen on parenthesis matching */
+#endif
+
+ replace_with_cached(cached_stack != NULL);
+
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE)
+ cached_stack = (void *)rbsearch(
+ new_state->s.stacks[a],
+ instance->stacks_tree
+ );
+
+ replace_with_cached(cached_stack != new_state->s.stacks[a]);
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE)
+ cached_stack = g_tree_lookup(
+ instance->stacks_tree,
+ (gpointer)new_state->s.stacks[a]
+ );
+
+ /* replace_with_cached contains an if statement */
+ replace_with_cached(cached_stack != NULL)
+ else
+ {
+ g_tree_insert(
+ instance->stacks_tree,
+ (gpointer)new_state->s.stacks[a],
+ (gpointer)new_state->s.stacks[a]
+ );
+ }
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH)
+ cached_stack = g_hash_table_lookup(
+ instance->stacks_hash,
+ (gconstpointer)new_state->s.stacks[a]
+ );
+ replace_with_cached(cached_stack != NULL)
+ else
+ {
+ g_hash_table_insert(
+ instance->stacks_hash,
+ (gpointer)new_state->s.stacks[a],
+ (gpointer)new_state->s.stacks[a]
+ );
+ }
+#endif
+ }
+}
+#else
+#define freecell_solver_cache_stacks(instance, new_state)
+#endif
+
+
+#ifdef FCS_WITH_TALONS
+void freecell_solver_cache_talon(
+ freecell_solver_instance_t * instance,
+ fcs_state_with_locations_t * new_state
+ )
+{
+ void * cached_talon;
+ SFO_hash_value_t hash_value_int;
+
+ new_state->s.talon = realloc(new_state->s.talon, fcs_klondike_talon_len(new_state->s)+1);
+#error Add Hash Code
+ hash_value_int = *(SFO_hash_value_t*)instance->hash_value;
+ if (hash_value_int < 0)
+ {
+ /*
+ * This is a bit mask that nullifies the sign bit of the
+ * number so it will always be positive
+ * */
+ hash_value_int &= (~(1<<((sizeof(hash_value_int)<<3)-1)));
+ }
+
+ cached_talon = (void *)freecell_solver_hash_insert(
+ instance->talons_hash,
+ new_state->s.talon,
+ hash_value_int,
+ 1
+ );
+
+ if (cached_talon != NULL)
+ {
+ free(new_state->s.talon);
+ new_state->s.talon = cached_talon;
+ }
+}
+#endif
+
+
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH)
+guint freecell_solver_hash_function(gconstpointer key)
+{
+ guint hash_value;
+ const char * s_ptr = (char*)key;
+ const char * s_end = s_ptr+sizeof(fcs_state_t);
+ hash_value = 0;
+ while (s_ptr < s_end)
+ {
+ hash_value += (hash_value << 5) + *(s_ptr++);
+ }
+ hash_value += (hash_value >> 5);
+
+ return hash_value;
+}
+#endif
+
+
+/*
+ * check_and_add_state() does the following things:
+ *
+ * 1. Check if the number of iterations exceeded its maximum, and if so
+ * return FCS_STATE_EXCEEDS_MAX_NUM_TIMES in order to terminate the
+ * solving process.
+ * 2. Check if the maximal depth was reached and if so return
+ * FCS_STATE_EXCEEDS_MAX_DEPTH
+ * 3. Canonize the state.
+ * 4. Check if the state is already found in the collection of the states
+ * that were already checked.
+ * If it is:
+ *
+ * 5a. Return FCS_STATE_ALREADY_EXISTS
+ *
+ * If it isn't:
+ *
+ * 5b. Call solve_for_state() on the board.
+ *
+ * */
+
+GCC_INLINE int freecell_solver_check_and_add_state(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * new_state,
+ fcs_state_with_locations_t * * existing_state
+ )
+{
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH)
+ SFO_hash_value_t hash_value_int;
+#endif
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT)
+ fcs_state_with_locations_t * * pos_ptr;
+ int found;
+#endif
+ freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread;
+ freecell_solver_instance_t * instance = hard_thread->instance;
+
+ int check;
+
+ if (check_if_limits_exceeded())
+ {
+ return FCS_STATE_BEGIN_SUSPEND_PROCESS;
+ }
+
+ freecell_solver_cache_stacks(hard_thread, new_state);
+
+ fcs_canonize_state(new_state, instance->freecells_num, instance->stacks_num);
+
+ fcs_caas_check_and_insert();
+ if (check)
+ {
+ /* The new state was not found in the cache, and it was already inserted */
+ if (new_state->parent)
+ {
+ new_state->parent->num_active_children++;
+ }
+ instance->num_states_in_collection++;
+
+ if (new_state->moves_to_parent != NULL)
+ {
+ new_state->moves_to_parent =
+ freecell_solver_move_stack_compact_allocate(
+ hard_thread,
+ new_state->moves_to_parent
+ );
+ }
+
+ return FCS_STATE_DOES_NOT_EXIST;
+ }
+ else
+ {
+ return FCS_STATE_ALREADY_EXISTS;
+ }
+}
+
+
+
+/*
+ * This implementation crashes for some reason, so don't use it.
+ *
+ * */
+
+
+#if 0
+
+static char meaningless_data[16] = "Hello World!";
+
+int freecell_solver_check_and_add_state(freecell_solver_instance_t * instance, fcs_state_with_locations_t * new_state, int depth)
+{
+ DBT key, value;
+
+ if ((instance->max_num_times >= 0) &&
+ (instance->max_num_times <= instance->num_times))
+ {
+ return FCS_STATE_EXCEEDS_MAX_NUM_TIMES;
+ }
+
+ if ((instance->max_depth >= 0) &&
+ (instance->max_depth <= depth))
+ {
+ return FCS_STATE_EXCEEDS_MAX_DEPTH;
+ }
+
+ fcs_canonize_state(new_state, instance->freecells_num, instance->stacks_num);
+
+ freecell_solver_cache_stacks(instance, new_state);
+
+ key.data = new_state;
+ key.size = sizeof(*new_state);
+
+ if (instance->db->get(
+ instance->db,
+ NULL,
+ &key,
+ &value,
+ 0
+ ) == 0)
+ {
+ /* The new state was not found. Let's insert it.
+ * The value should be non-NULL or else g_hash_table_lookup() will
+ * return NULL even if it exists. */
+
+ value.data = meaningless_data;
+ value.size = 8;
+ instance->db->put(
+ instance->db,
+ NULL,
+ &key,
+ &value,
+ 0);
+ if (freecell_solver_solve_for_state(instance, new_state, depth+1,0) == FCS_STATE_WAS_SOLVED)
+ {
+ return FCS_STATE_WAS_SOLVED;
+ }
+ else
+ {
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+ }
+ }
+ else
+ {
+ /* free (value.data) ; */
+ return FCS_STATE_ALREADY_EXISTS;
+ }
+}
+
+
+#endif
+
+#endif /* #ifndef FC_SOLVE__CAAS_C */
diff --git a/kpat/freecell-solver/caas.h b/kpat/freecell-solver/caas.h
new file mode 100644
index 00000000..e1969488
--- /dev/null
+++ b/kpat/freecell-solver/caas.h
@@ -0,0 +1,28 @@
+
+#ifndef FC_SOLVE__CAAS_H
+#define FC_SOLVE__CAAS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* #define FCS_USE_INLINE */
+
+/*
+ * check_and_add_state is defined in caas.c.
+ *
+ * DFS stands for Depth First Search which is the type of scan Freecell
+ * Solver uses to solve a given board.
+ * */
+
+extern int freecell_solver_check_and_add_state(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * new_state,
+ fcs_state_with_locations_t * * existing_state
+ );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef FC_SOLVE__CAAS_H */
diff --git a/kpat/freecell-solver/card.c b/kpat/freecell-solver/card.c
new file mode 100644
index 00000000..d4df80f7
--- /dev/null
+++ b/kpat/freecell-solver/card.c
@@ -0,0 +1,286 @@
+/*
+ * card.c - functions to convert cards and card components to and from
+ * its user representation.
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+
+#include <string.h>
+
+#include "card.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+
+#define uc(c) ( (((c)>='a') && ((c)<='z')) ? ((c)+'A'-'a') : (c))
+
+/*
+ * This function converts a card number from its user representation
+ * (e.g: "A", "K", "9") to its card number that can be used by
+ * the program.
+ * */
+int freecell_solver_u2p_card_number(const char * string)
+{
+ char rest;
+
+ while (1)
+ {
+ rest = uc(*string);
+
+ if ((rest == '\0') || (rest == ' ') || (rest == '\t'))
+ {
+ return 0;
+ }
+ if (rest == 'A')
+ {
+ return 1;
+ }
+ else if (rest =='J')
+ {
+ return 11;
+ }
+ else if (rest == 'Q')
+ {
+ return 12;
+ }
+ else if (rest == 'K')
+ {
+ return 13;
+ }
+ else if (rest == '1')
+ {
+ return (*(string+1) == '0')?10:1;
+ }
+ else if ((rest == '0') || (rest == 'T'))
+ {
+ return 10;
+ }
+ else if ((rest >= '2') && (rest <= '9'))
+ {
+ return (rest-'0');
+ }
+ else
+ {
+ string++;
+ }
+ }
+}
+
+
+/*
+ * This function converts a string containing a suit letter (that is
+ * one of H,S,D,C) into its suit ID.
+ *
+ * The suit letter may come somewhat after the beginning of the string.
+ *
+ * */
+int freecell_solver_u2p_suit(const char * suit)
+{
+ char c;
+
+ c = uc(*suit);
+ while (
+ (c != 'H') &&
+ (c != 'S') &&
+ (c != 'D') &&
+ (c != 'C') &&
+ (c != ' ') &&
+ (c != '\0'))
+ {
+ suit++;
+ c = uc(*suit);
+ }
+
+ if (c == 'H')
+ return 0;
+ else if (c == 'C')
+ return 1;
+ else if (c == 'D')
+ return 2;
+ else if (c == 'S')
+ return 3;
+ else
+ return 0;
+}
+
+static int fcs_u2p_flipped_status(const char * str)
+{
+ while (*str != '\0')
+ {
+ if ((*str != ' ') && (*str != '\t'))
+ {
+ return (*str == '<');
+ }
+ str++;
+ }
+ return 0;
+}
+/*
+ * This function converts an entire card from its string representations
+ * (e.g: "AH", "KS", "8D"), to a fcs_card_t data type.
+ * */
+fcs_card_t freecell_solver_card_user2perl(const char * str)
+{
+ fcs_card_t card;
+#if defined(COMPACT_STATES)||defined(INDIRECT_STACK_STATES)
+ card = 0;
+#endif
+ fcs_card_set_flipped(card, fcs_u2p_flipped_status(str));
+ fcs_card_set_num(card, fcs_u2p_card_number(str));
+ fcs_card_set_suit(card, fcs_u2p_suit(str));
+
+ return card;
+}
+
+
+/*
+ * Those strings contain the string representations of the different cards.
+ * If CARD_DEBUG_PRES is defined then an asterisk is printed as an empty card.
+ *
+ * Notice that there are two of them: one prints 10 and one prints T for the
+ * 10 card.
+ *
+ * */
+#ifdef CARD_DEBUG_PRES
+static char card_map_3_10[14][4] = { "*", "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" };
+
+static char card_map_3_T[14][4] = { "*", "A", "2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K" };
+
+#else
+static char card_map_3_10[14][4] = { " ", "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" };
+
+static char card_map_3_T[14][4] = { " ", "A", "2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K" };
+
+#endif
+
+/*
+ * Converts a card_number from its internal representation to a string.
+ *
+ * num - the card number
+ * str - the string to output to.
+ * card_num_is_null - a pointer to a bool that indicates whether
+ * the card number is out of range or equal to zero
+ * t - whether 10 should be printed as T or not.
+ * flipped - whether the card is face down
+ * */
+char * freecell_solver_p2u_card_number(
+ int num,
+ char * str,
+ int * card_num_is_null,
+ int t,
+ int flipped)
+{
+ char (*card_map_3) [4] = card_map_3_10;
+ if (t)
+ {
+ card_map_3 = card_map_3_T;
+ }
+#ifdef CARD_DEBUG_PRES
+ if (0)
+ {
+ }
+#else
+ if (flipped)
+ {
+ strncpy(str, "*", 2);
+ *card_num_is_null = 0;
+ }
+#endif
+ else
+ {
+ if ((num >= 0) && (num <= 13))
+ {
+ strncpy(str, card_map_3[num], strlen(card_map_3[num])+1);
+ *card_num_is_null = (num == 0);
+ }
+ else
+ {
+ strncpy(str, card_map_3[0], strlen(card_map_3[0])+1);
+ *card_num_is_null = 1;
+ }
+ }
+ return str;
+}
+
+/*
+ * Converts a suit to its user representation.
+ *
+ * */
+char * freecell_solver_p2u_suit(int suit, char * str, int card_num_is_null, int flipped)
+{
+#ifndef CARD_DEBUG_PRES
+ if (flipped)
+ {
+ strncpy(str, "*", 2);
+ }
+ else
+#endif
+ if (suit == 0)
+ {
+ if (card_num_is_null)
+#ifdef CARD_DEBUG_PRES
+ strncpy(str, "*", 2);
+#else
+ strncpy(str, " ", 2);
+#endif
+ else
+ strncpy(str, "H", 2);
+ }
+ else if (suit == 1)
+ strncpy(str, "C", 2);
+ else if (suit == 2)
+ strncpy(str, "D", 2);
+ else if (suit == 3)
+ strncpy(str, "S", 2);
+ else
+ strncpy(str, " ", 2);
+ return str;
+}
+
+/*
+ * Convert an entire card to its user representation.
+ *
+ * */
+char * freecell_solver_card_perl2user(fcs_card_t card, char * str, int t)
+{
+ int card_num_is_null;
+#ifdef CARD_DEBUG_PRES
+ if (fcs_card_get_flipped(card))
+ {
+ *str = '<';
+ str++;
+ }
+#endif
+
+ fcs_p2u_card_number(
+ fcs_card_card_num(card),
+ str,
+ &card_num_is_null,
+ t,
+ fcs_card_get_flipped(card)
+ );
+ /*
+ * Notice that if card_num_is_null is found to be true
+ * it will affect the output of the suit too.
+ *
+ * */
+ fcs_p2u_suit(
+ fcs_card_suit(card),
+ str+strlen(str),
+ card_num_is_null,
+ fcs_card_get_flipped(card)
+ );
+
+#ifdef CARD_DEBUG_PRES
+ if (fcs_card_get_flipped(card))
+ {
+ strcat(str, ">");
+ }
+#endif
+
+ return str;
+}
diff --git a/kpat/freecell-solver/card.h b/kpat/freecell-solver/card.h
new file mode 100644
index 00000000..d67ea645
--- /dev/null
+++ b/kpat/freecell-solver/card.h
@@ -0,0 +1,100 @@
+/*
+ * card.h - header file for card functions for Freecell Solver
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+
+
+#ifndef FC_SOLVE__CARD_H
+#define FC_SOLVE__CARD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef FC_SOLVE__STATE_H
+#include "state.h"
+#endif
+
+/*
+ * This function converts an entire card from its string representations
+ * (e.g: "AH", "KS", "8D"), to a fcs_card_t data type.
+ * */
+extern fcs_card_t freecell_solver_card_user2perl(const char * str);
+#define fcs_card_user2perl(str) (freecell_solver_card_user2perl(str))
+
+
+
+/*
+ * Convert an entire card to its user representation.
+ *
+ * */
+extern char * freecell_solver_card_perl2user(
+ fcs_card_t card,
+ char * str,
+ int t
+ );
+
+#define fcs_card_perl2user(card,str,t) (freecell_solver_card_perl2user((card),(str),(t)))
+
+
+
+/*
+ * Converts a card_number from its internal representation to a string.
+ *
+ * num - the card number
+ * str - the string to output to.
+ * card_num_is_null - a pointer to a bool that indicates whether
+ * the card number is out of range or equal to zero
+ * t - whether 10 should be printed as T or not.
+ * */
+extern char * freecell_solver_p2u_card_number(
+ int num,
+ char * str,
+ int * card_num_is_null,
+ int t,
+ int flipped
+ );
+
+#define fcs_p2u_card_number(num,str,card_num_is_null,t,flipped) \
+ (freecell_solver_p2u_card_number((num),(str),(card_num_is_null),(t),(flipped)))
+
+/*
+ * Converts a suit to its user representation.
+ *
+ * */
+char * freecell_solver_p2u_suit(
+ int suit,
+ char * str,
+ int card_num_is_null,
+ int flipped
+ );
+
+#define fcs_p2u_suit(suit,str,card_num_is_null,flipped) \
+ (freecell_solver_p2u_suit((suit),(str),(card_num_is_null),(flipped)))
+
+/*
+ * This function converts a card number from its user representation
+ * (e.g: "A", "K", "9") to its card number that can be used by
+ * the program.
+ * */
+extern int freecell_solver_u2p_card_number(const char * string);
+#define fcs_u2p_card_number(string) (freecell_solver_u2p_card_number(string))
+
+/*
+ * This function converts a string containing a suit letter (that is
+ * one of H,S,D,C) into its suit ID.
+ *
+ * The suit letter may come somewhat after the beginning of the string.
+ *
+ * */
+extern int freecell_solver_u2p_suit(const char * deck);
+#define fcs_u2p_suit(deck) (freecell_solver_u2p_suit(deck))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FC_SOLVE__CARD_H */
diff --git a/kpat/freecell-solver/cl_chop.c b/kpat/freecell-solver/cl_chop.c
new file mode 100644
index 00000000..4bb82aab
--- /dev/null
+++ b/kpat/freecell-solver/cl_chop.c
@@ -0,0 +1,245 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cl_chop.h"
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+#define ARGS_MAN_GROW_BY 30
+
+args_man_t * freecell_solver_args_man_alloc(void)
+{
+ args_man_t * ret;
+ ret = malloc(sizeof(args_man_t));
+ ret->argc = 0;
+ ret->max_num_argv = ARGS_MAN_GROW_BY;
+ ret->argv = malloc(sizeof(ret->argv[0]) * ret->max_num_argv);
+ return ret;
+}
+
+void freecell_solver_args_man_free(args_man_t * manager)
+{
+ int a;
+ for(a=0;a<manager->argc;a++)
+ {
+ free(manager->argv[a]);
+ }
+ free(manager->argv);
+ free(manager);
+}
+
+#define skip_ws() { while((*s == ' ') || (*s == '\t')) { s++; } }
+#define skip_non_ws() { while((*s != ' ') && (*s != '\t') && (*s)) { s++; }}
+
+#define add_to_last_arg(c) \
+ { \
+ *(last_arg_ptr++) = (c); \
+ if (last_arg_ptr == last_arg_end) \
+ { \
+ new_last_arg = realloc(last_arg, (size_t)(last_arg_end-last_arg+1024)); \
+ last_arg_ptr += new_last_arg - last_arg; \
+ last_arg_end += new_last_arg - last_arg + 1024; \
+ last_arg = new_last_arg; \
+ } \
+ }
+
+#define push_args_last_arg() { \
+ new_arg = malloc((size_t)(last_arg_ptr-last_arg+1)); \
+ strncpy(new_arg, last_arg, (size_t)(last_arg_ptr-last_arg)); \
+ new_arg[last_arg_ptr-last_arg] = '\0'; \
+ manager->argv[manager->argc] = new_arg; \
+ manager->argc++; \
+ if (manager->argc == manager->max_num_argv) \
+ { \
+ manager->max_num_argv += ARGS_MAN_GROW_BY; \
+ manager->argv = realloc(manager->argv, sizeof(manager->argv[0]) * manager->max_num_argv); \
+ } \
+ \
+ /* Reset last_arg_ptr so we will have an entirely new argument */ \
+ last_arg_ptr = last_arg; \
+ }
+
+#define is_whitespace(c) \
+ (((c) == ' ') || ((c) == '\t') || ((c) == '\n') || ((c) == '\r'))
+
+int freecell_solver_args_man_chop(args_man_t * manager, char * string)
+{
+ char * s = string;
+ char * new_arg;
+ char * last_arg, * last_arg_ptr, * last_arg_end, * new_last_arg;
+ char next_char;
+ int in_arg;
+
+ last_arg_ptr = last_arg = malloc(1024);
+ last_arg_end = last_arg + 1023;
+
+ while (*s != '\0')
+ {
+LOOP_START:
+ in_arg = 0;
+ while (is_whitespace(*s))
+ {
+ s++;
+ }
+ if (*s == '\0')
+ {
+ break;
+ }
+ if (*s == '#')
+ {
+ in_arg = 0;
+ /* Skip to the next line */
+ while((*s != '\0') && (*s != '\n'))
+ {
+ s++;
+ }
+ continue;
+ }
+AFTER_WS:
+ while ((*s != ' ') && (*s != '\t') && (*s != '\n') &&
+ (*s != '\r') &&
+ (*s != '\\') && (*s != '\"') && (*s != '\0') &&
+ (*s != '#'))
+ {
+ in_arg = 1;
+ add_to_last_arg(*s);
+ s++;
+ }
+
+
+ if ((*s == ' ') || (*s == '\t') || (*s == '\n') || (*s == '\0') || (*s == '\r'))
+ {
+NEXT_ARG:
+ push_args_last_arg();
+ in_arg = 0;
+
+ if (*s == '\0')
+ {
+ break;
+ }
+ }
+ else if (*s == '\\')
+ {
+ char next_char = *(++s);
+ s++;
+ if (next_char == '\0')
+ {
+ s--;
+ goto NEXT_ARG;
+ }
+ else if ((next_char == '\n') || (next_char == '\r'))
+ {
+ if (in_arg)
+ {
+ goto AFTER_WS;
+ }
+ else
+ {
+ goto LOOP_START;
+ }
+ }
+ else
+ {
+ add_to_last_arg(next_char);
+ }
+ }
+ else if (*s == '\"')
+ {
+ s++;
+ in_arg = 1;
+ while ((*s != '\"') && (*s != '\0'))
+ {
+ if (*s == '\\')
+ {
+ next_char = *(++s);
+ if (next_char == '\0')
+ {
+ push_args_last_arg();
+
+ goto END_OF_LOOP;
+ }
+ else if ((next_char == '\n') || (next_char == '\r'))
+ {
+ /* Do nothing */
+ }
+ else if ((next_char == '\\') || (next_char == '\"'))
+ {
+ add_to_last_arg(next_char);
+ }
+ else
+ {
+ add_to_last_arg('\\');
+ add_to_last_arg(next_char);
+ }
+ }
+ else
+ {
+ add_to_last_arg(*s);
+ }
+ s++;
+ }
+ s++;
+ goto AFTER_WS;
+ }
+ else if (*s == '#')
+ {
+ in_arg = 0;
+ /* Skip to the next line */
+ while((*s != '\0') && (*s != '\n'))
+ {
+ s++;
+ }
+ goto NEXT_ARG;
+ }
+ }
+END_OF_LOOP:
+
+ free(last_arg);
+
+ return 0;
+}
+
+#ifdef CMD_LINE_CHOP_WITH_MAIN
+int main(int argc, char * * argv)
+{
+ args_man_t * args_man;
+ char * string;
+
+#if 0
+ string = argv[1];
+#else
+ {
+ FILE * f;
+
+ f = fopen(argv[1],"rb");
+ string = calloc(4096,1);
+ fread(string, 4095, 1, f);
+ fclose(f);
+ }
+
+#endif
+
+ /* Initialize an arg man */
+ args_man = args_man_alloc();
+ /* Call it on string */
+ args_man_chop(args_man, string);
+
+ /* Now use args_man->argc and args_man->argv */
+ {
+ int a;
+ for(a=0;a<args_man->argc;a++)
+ {
+ printf("argv[%i] = \"%s\"\n", a, args_man->argv[a]);
+ }
+ }
+ /* Free the allocated memory */
+ args_man_free(args_man);
+
+ free(string);
+
+ return 0;
+}
+#endif
diff --git a/kpat/freecell-solver/cl_chop.h b/kpat/freecell-solver/cl_chop.h
new file mode 100644
index 00000000..3f6a873e
--- /dev/null
+++ b/kpat/freecell-solver/cl_chop.h
@@ -0,0 +1,19 @@
+
+#ifndef FC_SOLVE__CMD_LINE_CHOP_H
+#define FC_SOLVE__CMD_LINE_CHOP_H
+
+struct args_man_struct
+{
+ int argc;
+ char * * argv;
+ int max_num_argv;
+};
+
+typedef struct args_man_struct args_man_t;
+
+extern args_man_t * freecell_solver_args_man_alloc(void);
+extern void freecell_solver_args_man_free(args_man_t * manager);
+extern int freecell_solver_args_man_chop(args_man_t * manager, char * string);
+
+#endif /* #ifndef FC_SOLVE__CMD_LINE_CHOP_H */
+
diff --git a/kpat/freecell-solver/cmd_line.c b/kpat/freecell-solver/cmd_line.c
new file mode 100644
index 00000000..63fbf6c9
--- /dev/null
+++ b/kpat/freecell-solver/cmd_line.c
@@ -0,0 +1,964 @@
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "fcs_user.h"
+#include "fcs_cl.h"
+#include "cl_chop.h"
+
+#include "prefix.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+static int read_preset(char * preset_name, args_man_t * * args, char * * opened_files_dir_to_assign, char * user_preset_dir)
+{
+ int ret_code = 1;
+ char * home_dir_presetrc = NULL, * global_presetrc = NULL, * env_var_presetrc = NULL;
+ char * path;
+ char * * presetrc_pathes[5] = {&env_var_presetrc, &home_dir_presetrc, &global_presetrc, &user_preset_dir, NULL};
+ int path_idx;
+ char line[8192];
+ FILE * f = NULL;
+ char * fgets_ret;
+ char * opened_files_dir = NULL;
+ int read_next_preset = 0;
+
+ {
+ char * home_dir;
+ home_dir = getenv("HOME");
+ if (home_dir)
+ {
+ home_dir_presetrc = malloc(strlen(home_dir) + 50);
+ sprintf(home_dir_presetrc,
+ "%s/.freecell-solver/presetrc", home_dir
+ );
+ }
+ }
+ env_var_presetrc = getenv("FREECELL_SOLVER_PRESETRC");
+
+ global_presetrc = (FREECELL_SOLVER_PKG_DATA_DIR "/presetrc");
+
+ for(path_idx=0;(presetrc_pathes[path_idx] != NULL) ; path_idx++)
+ {
+ path = (*presetrc_pathes[path_idx]);
+ if (path == NULL)
+ {
+ continue;
+ }
+ f = fopen(path, "rt");
+ if (f == NULL)
+ {
+ continue;
+ }
+ while(1)
+ {
+ fgets_ret = fgets(line, sizeof(line), f);
+ if (fgets_ret == NULL)
+ {
+ break;
+ }
+ if (!strncmp(line, "dir=", 4))
+ {
+#define nullify_newline() \
+ { \
+ char * s; \
+ \
+ s = strchr(line, '\n'); \
+ if (s != NULL) \
+ { \
+ *s = '\0'; \
+ } \
+ }
+ nullify_newline();
+
+ if (opened_files_dir != NULL)
+ {
+ free(opened_files_dir);
+ }
+ opened_files_dir = strdup(line+4);
+ }
+ else if (!strncmp(line, "name=", 5))
+ {
+ nullify_newline();
+ if (!strcmp(line+5, preset_name))
+ {
+ read_next_preset = 1;
+ }
+ }
+ else if (!strncmp(line, "command=", 8))
+ {
+ if (read_next_preset)
+ {
+ *args = freecell_solver_args_man_alloc();
+ freecell_solver_args_man_chop(*args, line+8);
+ ret_code = 0;
+ goto HAVE_PRESET;
+ }
+ }
+ }
+ fclose(f);
+ f = NULL;
+#undef nullify_newline
+ }
+HAVE_PRESET:
+
+ if (f)
+ {
+ fclose(f);
+ }
+
+ if (home_dir_presetrc)
+ {
+ free(home_dir_presetrc);
+ }
+
+ if (ret_code == 0)
+ {
+ *opened_files_dir_to_assign = opened_files_dir;
+ }
+ else
+ {
+ if (opened_files_dir)
+ {
+ free(opened_files_dir);
+ }
+ }
+
+ return ret_code;
+}
+
+
+int freecell_solver_user_cmd_line_parse_args_with_file_nesting_count(
+ void * instance,
+ int argc,
+ const char * argv[],
+ int start_arg,
+ char * * known_parameters,
+ freecell_solver_user_cmd_line_known_commands_callback_t callback,
+ void * callback_context,
+ char * * error_string,
+ int * last_arg,
+ int file_nesting_count,
+ char * opened_files_dir
+ )
+{
+ int arg;
+ char * * known_param;
+ int num_to_skip;
+ int callback_ret;
+ int ret;
+
+ *error_string = NULL;
+
+ for(arg=start_arg;arg<argc;arg++)
+ {
+ /* First check for the parameters that the user recognizes */
+ known_param = known_parameters;
+ while((*known_param) && strcmp(*known_param, argv[arg]))
+ {
+ known_param++;
+ }
+ if ((*known_param) != NULL )
+ {
+ callback_ret = callback(instance, argc, argv, arg, &num_to_skip, &ret, callback_context);
+ if (callback_ret == FCS_CMD_LINE_SKIP)
+ {
+ arg += num_to_skip-1;
+ continue;
+ }
+ else if (callback_ret == FCS_CMD_LINE_STOP)
+ {
+ *last_arg = arg;
+ return ret;
+ }
+ }
+
+ if (0)
+ {
+ }
+ else if ((!strcmp(argv[arg], "-md")) || (!strcmp(argv[arg], "--max-depth")))
+ {
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+ freecell_solver_user_limit_depth(instance, atoi(argv[arg]));
+ }
+ else if ((!strcmp(argv[arg], "-mi")) || (!strcmp(argv[arg], "--max-iters")))
+ {
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+
+ freecell_solver_user_limit_current_instance_iterations(instance, atoi(argv[arg]));
+ }
+ else if ((!strcmp(argv[arg], "-to")) || (!strcmp(argv[arg], "--tests-order")))
+ {
+ char * fcs_user_errstr;
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+ ret = freecell_solver_user_set_tests_order(instance, argv[arg], &fcs_user_errstr);
+ if (ret != 0)
+ {
+ char * errstr = malloc(strlen(fcs_user_errstr)+500);
+ sprintf(
+ errstr,
+ "Error in tests' order!\n%s\n",
+ fcs_user_errstr
+ );
+ free(fcs_user_errstr);
+
+ *error_string = errstr;
+
+ *last_arg = arg;
+ return FCS_CMD_LINE_ERROR_IN_ARG;
+ }
+ }
+ else if ((!strcmp(argv[arg], "--freecells-num")))
+ {
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+ if (freecell_solver_user_set_num_freecells(instance, atoi(argv[arg])) != 0)
+ {
+ char * errstr;
+
+ errstr = malloc(500);
+ sprintf(errstr,
+ "Error! The freecells\' number "
+ "exceeds the maximum of %i.\n"
+ "Recompile the program if you wish to have more.\n",
+ freecell_solver_user_get_max_num_freecells()
+ );
+
+ *error_string = errstr;
+
+ *last_arg = arg;
+ return FCS_CMD_LINE_ERROR_IN_ARG;
+ }
+ }
+ else if ((!strcmp(argv[arg], "--stacks-num")))
+ {
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+ if (freecell_solver_user_set_num_stacks(instance, atoi(argv[arg])) != 0)
+ {
+ char * errstr;
+
+ errstr = malloc(500);
+ sprintf(errstr,
+ "Error! The stacks\' number "
+ "exceeds the maximum of %i.\n"
+ "Recompile the program if you wish to have more.\n",
+ freecell_solver_user_get_max_num_stacks()
+ );
+
+ *error_string = errstr;
+
+ *last_arg = arg;
+ return FCS_CMD_LINE_ERROR_IN_ARG;
+ }
+ }
+ else if ((!strcmp(argv[arg], "--decks-num")))
+ {
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+ if (freecell_solver_user_set_num_decks(instance, atoi(argv[arg])) != 0)
+ {
+ char * errstr;
+
+ errstr = malloc(500);
+ sprintf(errstr,
+ "Error! The decks\' number "
+ "exceeds the maximum of %i.\n"
+ "Recopmile the program if you wish to have more.\n",
+ freecell_solver_user_get_max_num_decks()
+ );
+
+ *error_string = errstr;
+
+ *last_arg = arg;
+ return FCS_CMD_LINE_ERROR_IN_ARG;
+ }
+ }
+ else if ((!strcmp(argv[arg], "--sequences-are-built-by")))
+ {
+ int sbb;
+
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+
+ if (!strcmp(argv[arg], "suit"))
+ {
+ sbb = FCS_SEQ_BUILT_BY_SUIT;
+ }
+ else if (!strcmp(argv[arg], "rank"))
+ {
+ sbb = FCS_SEQ_BUILT_BY_RANK;
+ }
+ else
+ {
+ sbb = FCS_SEQ_BUILT_BY_ALTERNATE_COLOR;
+ }
+ freecell_solver_user_set_sequences_are_built_by_type(instance, sbb);
+ }
+ else if ((!strcmp(argv[arg], "--sequence-move")))
+ {
+ int unlimited;
+
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+
+ if (!strcmp(argv[arg], "unlimited"))
+ {
+ unlimited = 1;
+ }
+ else
+ {
+ unlimited = 0;
+ }
+ freecell_solver_user_set_sequence_move(instance, unlimited);
+ }
+ else if (!strcmp(argv[arg], "--empty-stacks-filled-by"))
+ {
+ int es_fill;
+
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+
+ if (!strcmp(argv[arg], "kings"))
+ {
+ es_fill = FCS_ES_FILLED_BY_KINGS_ONLY;
+ }
+ else if (!strcmp(argv[arg], "none"))
+ {
+ es_fill = FCS_ES_FILLED_BY_NONE;
+ }
+ else
+ {
+ es_fill = FCS_ES_FILLED_BY_ANY_CARD;
+ }
+ freecell_solver_user_set_empty_stacks_filled_by(
+ instance,
+ es_fill
+ );
+ }
+ else if (
+ (!strcmp(argv[arg], "--game")) ||
+ (!strcmp(argv[arg], "--preset")) ||
+ (!strcmp(argv[arg], "-g"))
+ )
+ {
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+
+ ret = freecell_solver_user_apply_preset(instance, argv[arg]);
+ if (ret == FCS_PRESET_CODE_NOT_FOUND)
+ {
+ char * errstr;
+
+ errstr = malloc(strlen(argv[arg])+500);
+
+ sprintf(errstr, "Unknown game \"%s\"!\n\n", argv[arg]);
+ *error_string = errstr;
+
+ *last_arg = arg;
+ return FCS_CMD_LINE_ERROR_IN_ARG;
+ }
+ else if (ret == FCS_PRESET_CODE_FREECELLS_EXCEED_MAX)
+ {
+ char * errstr;
+
+ errstr = malloc(strlen(argv[arg])+500);
+ sprintf(errstr, "The game \"%s\" exceeds the maximal number "
+ "of freecells in the program.\n"
+ "Modify the file \"config.h\" and recompile, "
+ "if you wish to solve one of its boards.\n",
+ argv[arg]
+ );
+
+ *error_string = errstr;
+
+ *last_arg = arg;
+ return FCS_CMD_LINE_ERROR_IN_ARG;
+ }
+ else if (ret == FCS_PRESET_CODE_STACKS_EXCEED_MAX)
+ {
+ char * errstr;
+
+ errstr = malloc(strlen(argv[arg])+500);
+
+ sprintf(errstr, "The game \"%s\" exceeds the maximal number "
+ "of stacks in the program.\n"
+ "Modify the file \"config.h\" and recompile, "
+ "if you wish to solve one of its boards.\n",
+ argv[arg]
+ );
+
+ *error_string = errstr;
+
+ *last_arg = arg;
+ return FCS_CMD_LINE_ERROR_IN_ARG;
+ }
+ else if (ret != FCS_PRESET_CODE_OK)
+ {
+ char * errstr;
+
+ errstr = malloc(strlen(argv[arg])+500);
+
+ sprintf(errstr,
+ "The game \"%s\" exceeds the limits of the program.\n"
+ "Modify the file \"config.h\" and recompile, if you wish to solve one of its boards.\n",
+ argv[arg]
+ );
+
+ *error_string = errstr;
+
+ *last_arg = arg;
+ return FCS_CMD_LINE_ERROR_IN_ARG;
+ }
+ }
+ else if ((!strcmp(argv[arg], "-me")) || (!strcmp(argv[arg], "--method")))
+ {
+ int method;
+
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+ if (!strcmp(argv[arg], "dfs"))
+ {
+ method = FCS_METHOD_HARD_DFS;
+ }
+ else if (!strcmp(argv[arg], "soft-dfs"))
+ {
+ method = FCS_METHOD_SOFT_DFS;
+ }
+ else if (!strcmp(argv[arg], "bfs"))
+ {
+ method = FCS_METHOD_BFS;
+ }
+ else if (!strcmp(argv[arg], "a-star"))
+ {
+ method = FCS_METHOD_A_STAR;
+ }
+ else if (!strcmp(argv[arg], "random-dfs"))
+ {
+ method = FCS_METHOD_RANDOM_DFS;
+ }
+ else
+ {
+ char * errstr;
+
+ errstr = malloc(strlen(argv[arg])+500);
+
+ sprintf(
+ errstr,
+ "Unknown solving method \"%s\".\n",
+ argv[arg]
+ );
+
+ *error_string = errstr;
+
+ *last_arg = arg;
+ return FCS_CMD_LINE_ERROR_IN_ARG;
+ }
+
+ freecell_solver_user_set_solving_method(instance, method);
+ }
+ else if ((!strcmp(argv[arg], "-asw")) || (!strcmp(argv[arg], "--a-star-weights")))
+ {
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+ {
+ int a;
+ const char * start_num;
+ char * end_num;
+ char * num_copy;
+ start_num = argv[arg];
+ for(a=0;a<5;a++)
+ {
+ while ((*start_num > '9') && (*start_num < '0') && (*start_num != '\0'))
+ {
+ start_num++;
+ }
+ if (*start_num == '\0')
+ {
+ break;
+ }
+ end_num = start_num+1;
+ while ((((*end_num >= '0') && (*end_num <= '9')) || (*end_num == '.')) && (*end_num != '\0'))
+ {
+ end_num++;
+ }
+ num_copy = malloc(end_num-start_num+1);
+ memcpy(num_copy, start_num, end_num-start_num);
+ num_copy[end_num-start_num] = '\0';
+ freecell_solver_user_set_a_star_weight(
+ instance,
+ a,
+ atof(num_copy)
+ );
+ free(num_copy);
+ start_num=end_num+1;
+ }
+ }
+ }
+ else if ((!strcmp(argv[arg], "-opt")) || (!strcmp(argv[arg], "--optimize-solution")))
+ {
+ freecell_solver_user_set_solution_optimization(instance, 1);
+ }
+ else if ((!strcmp(argv[arg], "-seed")))
+ {
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+
+ freecell_solver_user_set_random_seed(instance, atoi(argv[arg]));
+ }
+ else if ((!strcmp(argv[arg], "-mss")) || (!strcmp(argv[arg], "--max-stored-states")))
+ {
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+
+ freecell_solver_user_limit_num_states_in_collection(
+ instance,
+ atoi(argv[arg])
+ );
+ }
+ else if (
+ (!strcmp(argv[arg], "-nst")) ||
+ (!strcmp(argv[arg], "--next-soft-thread")) ||
+ (!strcmp(argv[arg], "-nht")) ||
+ (!strcmp(argv[arg], "--next-hard-thread"))
+ )
+ {
+ int ret;
+ int is_st = ((!strcmp(argv[arg], "-nst")) || (!strcmp(argv[arg], "--next-soft-thread")));
+
+ ret =
+ is_st ?
+ freecell_solver_user_next_soft_thread(instance) :
+ freecell_solver_user_next_hard_thread(instance)
+ ;
+
+ if (ret)
+ {
+ char * errstr;
+
+ errstr = strdup("The maximal number of soft threads has been exceeded\n");
+
+ *error_string = errstr;
+
+ *last_arg = arg;
+
+ return FCS_CMD_LINE_ERROR_IN_ARG;
+ }
+ }
+ else if ((!strcmp(argv[arg], "-step")) || (!strcmp(argv[arg], "--soft-thread-step")))
+ {
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+
+ freecell_solver_user_set_soft_thread_step(
+ instance,
+ atoi(argv[arg])
+ );
+ }
+ else if ((!strcmp(argv[arg], "--reparent-states")))
+ {
+ freecell_solver_user_set_reparent_states(
+ instance,
+ 1
+ );
+ }
+ else if ((!strcmp(argv[arg], "--calc-real-depth")))
+ {
+ freecell_solver_user_set_calc_real_depth(
+ instance,
+ 1);
+ }
+ else if ((!strcmp(argv[arg], "--st-name")))
+ {
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+ freecell_solver_user_set_soft_thread_name(instance, argv[arg]);
+ }
+ else if ((!strcmp(argv[arg], "--prelude")))
+ {
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+ freecell_solver_user_set_hard_thread_prelude(instance, argv[arg]);
+ }
+ else if ((!strcmp(argv[arg], "-opt-to")) || (!strcmp(argv[arg], "--optimization-tests-order")))
+ {
+ char * fcs_user_errstr;
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+
+ ret = freecell_solver_user_set_optimization_scan_tests_order(
+ instance,
+ argv[arg],
+ &fcs_user_errstr
+ );
+
+ if (ret != 0)
+ {
+ char * errstr = malloc(strlen(fcs_user_errstr)+500);
+ sprintf(
+ errstr,
+ "Error in the optimization scan's tests' order!\n%s\n",
+ fcs_user_errstr
+ );
+ free(fcs_user_errstr);
+
+ *error_string = errstr;
+
+ *last_arg = arg;
+ return FCS_CMD_LINE_ERROR_IN_ARG;
+ }
+ }
+ else if ((!strcmp(argv[arg], "--scans-synergy")))
+ {
+ int value;
+
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+
+ if (!strcmp(argv[arg], "none"))
+ {
+ value = 0;
+ }
+ else if (!strcmp(argv[arg], "dead-end-marks"))
+ {
+ value = 1;
+ }
+ else
+ {
+ char * errstr;
+
+ errstr = malloc(strlen(argv[arg])+500);
+
+ sprintf(errstr, "Unknown scans' synergy type \"%s\"!\n", argv[arg]);
+ *last_arg = arg;
+ *error_string = errstr;
+ return FCS_CMD_LINE_ERROR_IN_ARG;
+ }
+
+ freecell_solver_user_set_scans_synergy(
+ instance,
+ value
+ );
+ }
+ else if ((!strcmp(argv[arg], "-ni")) ||
+ (!strcmp(argv[arg], "--next-instance")))
+ {
+ freecell_solver_user_next_instance(instance);
+ }
+ else if (!strcmp(argv[arg], "--reset"))
+ {
+ freecell_solver_user_reset(instance);
+ }
+ else if (!strcmp(argv[arg], "--read-from-file"))
+ {
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+ if (file_nesting_count == 0)
+ {
+ /* do nothing */
+ }
+ else
+ {
+ int num_to_skip = 0;
+ char * s, * buffer;
+ FILE * f;
+ long file_len;
+ int ret;
+ size_t num_read;
+ args_man_t * args_man;
+
+ s = argv[arg];
+ while(isdigit(*s))
+ {
+ s++;
+ }
+ if (*s == ',')
+ {
+ num_to_skip = atoi(argv[arg]);
+ s++;
+ }
+
+ if (opened_files_dir)
+ {
+ char * complete_path;
+
+ complete_path = malloc(strlen(opened_files_dir)+strlen(s)+1);
+ sprintf(complete_path, "%s%s", opened_files_dir, s);
+ f = fopen(complete_path, "rt");
+ free(complete_path);
+ }
+ else
+ {
+ /*
+ * Initialize f to NULL so it will be initialized
+ * */
+ f = NULL;
+ }
+
+ /* Try to open from the local path */
+ if (f == NULL)
+ {
+ f = fopen(s, "rt");
+ }
+
+ /* If we still could not open it return an error */
+ if (f == NULL)
+ {
+ char * err_str;
+
+ err_str = malloc(strlen(s)+100);
+ sprintf(err_str,
+ "Could not open file \"%s\"!\nQuitting.\n",
+ s);
+
+ *error_string = err_str;
+ *last_arg = arg;
+
+ return FCS_CMD_LINE_ERROR_IN_ARG;
+ }
+ fseek(f, 0, SEEK_END);
+ file_len = ftell(f);
+ buffer=malloc(file_len+1);
+ if (buffer == NULL)
+ {
+ *error_string = strdup("Could not allocate enough memory to parse the file. Quitting.\n");
+ fclose(f);
+
+ *last_arg = arg;
+
+ return FCS_CMD_LINE_ERROR_IN_ARG;
+ }
+ fseek(f,0,SEEK_SET);
+ num_read = fread(buffer, 1, file_len, f);
+ fclose(f);
+ buffer[num_read] = '\0';
+
+ args_man = freecell_solver_args_man_alloc();
+ ret = freecell_solver_args_man_chop(args_man, buffer);
+ free(buffer);
+ if (ret != 0)
+ {
+ *error_string =
+ strdup("Could not parse the file. Quitting\n");
+ freecell_solver_args_man_free(args_man);
+
+ *last_arg = arg;
+
+ return FCS_CMD_LINE_ERROR_IN_ARG;
+ }
+
+ if (num_to_skip >= args_man->argc)
+ {
+ /* Do nothing */
+ }
+ else
+ {
+ ret = freecell_solver_user_cmd_line_parse_args_with_file_nesting_count(
+ instance,
+ args_man->argc - num_to_skip,
+ args_man->argv + num_to_skip,
+ 0,
+ known_parameters,
+ callback,
+ callback_context,
+ error_string,
+ last_arg,
+ ((file_nesting_count < 0) ? file_nesting_count : (file_nesting_count-1)),
+ opened_files_dir
+ );
+
+ if (ret == FCS_CMD_LINE_UNRECOGNIZED_OPTION)
+ {
+ /* Do nothing - continue */
+ }
+ else if (ret != FCS_CMD_LINE_OK)
+ {
+ freecell_solver_args_man_free(args_man);
+ return ret;
+ }
+ }
+ freecell_solver_args_man_free(args_man);
+ }
+ }
+ else if ((!strcmp(argv[arg], "-l")) || (!strcmp(argv[arg], "--load-config")))
+ {
+ arg++;
+ if (arg == argc)
+ {
+ *last_arg = arg-1;
+ return FCS_CMD_LINE_PARAM_WITH_NO_ARG;
+ }
+ {
+ int status;
+ args_man_t * preset_args = 0;
+ char * dir = NULL;
+
+ status = read_preset(argv[arg], &preset_args, &dir, NULL);
+ if (status != 0)
+ {
+ char * err_str;
+ err_str = malloc(strlen(argv[arg]) + 100);
+ sprintf(err_str, "Unable to load the \"%s\" configuration!\n", argv[arg]);
+ *error_string = err_str;
+
+ *last_arg = arg;
+
+ return FCS_CMD_LINE_ERROR_IN_ARG;
+ }
+ else
+ {
+ ret = freecell_solver_user_cmd_line_parse_args_with_file_nesting_count(
+ instance,
+ preset_args->argc,
+ preset_args->argv,
+ 0,
+ known_parameters,
+ callback,
+ callback_context,
+ error_string,
+ last_arg,
+ ((file_nesting_count < 0) ? file_nesting_count : (file_nesting_count-1)),
+ dir ? dir : opened_files_dir
+ );
+
+ if (dir)
+ {
+ free(dir);
+ }
+ freecell_solver_args_man_free(preset_args);
+
+ if (ret == FCS_CMD_LINE_UNRECOGNIZED_OPTION)
+ {
+ /* Do nothing - continue */
+ }
+ else if (ret != FCS_CMD_LINE_OK)
+ {
+ return ret;
+ }
+ }
+ }
+ }
+ else
+ {
+ *last_arg = arg;
+ return FCS_CMD_LINE_UNRECOGNIZED_OPTION;
+ }
+ }
+
+ *last_arg = arg;
+ return FCS_CMD_LINE_OK;
+}
+
+int freecell_solver_user_cmd_line_parse_args(
+ void * instance,
+ int argc,
+ const char * argv[],
+ int start_arg,
+ char * * known_parameters,
+ freecell_solver_user_cmd_line_known_commands_callback_t callback,
+ void * callback_context,
+ char * * error_string,
+ int * last_arg
+ )
+{
+ return freecell_solver_user_cmd_line_parse_args_with_file_nesting_count(
+ instance,
+ argc,
+ argv,
+ start_arg,
+ known_parameters,
+ callback,
+ callback_context,
+ error_string,
+ last_arg,
+ -1,
+ NULL
+ );
+}
+
diff --git a/kpat/freecell-solver/fcs.h b/kpat/freecell-solver/fcs.h
new file mode 100644
index 00000000..43300310
--- /dev/null
+++ b/kpat/freecell-solver/fcs.h
@@ -0,0 +1,797 @@
+/*
+ * fcs.h - header file of freecell_solver_instance and of user-level
+ * functions for Freecell Solver
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+
+#ifndef FC_SOLVE__FCS_H
+#define FC_SOLVE__FCS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "fcs_config.h"
+#include "state.h"
+#include "move.h"
+#include "fcs_enums.h"
+
+#include "rand.h"
+
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE) || (defined(INDIRECT_STACK_STATES) && (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE))
+
+#include <redblack.h>
+
+#endif
+
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) || (defined(INDIRECT_STACK_STATES) && (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE))
+
+#include <avl.h>
+
+#endif
+
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE) || (defined(INDIRECT_STACK_STATES) && (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE))
+
+#include <rb.h>
+
+/* #define TREE_IMP_PREFIX(func_name) rb_##func_name */
+
+#endif
+
+
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_TREE) || (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH) || (defined(INDIRECT_STACK_STATES) && ((FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE) || (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH)))
+
+#include <glib.h>
+
+#endif
+
+
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH)
+
+#include "fcs_hash.h"
+
+#endif
+
+#ifdef INDIRECT_STACK_STATES
+#include "fcs_hash.h"
+
+#endif
+
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_DB_FILE)
+#include <sys/types.h>
+#include <limits.h>
+#include <db.h>
+#endif
+
+#include "pqueue.h"
+
+#include "alloc.h"
+
+/*
+ * This is a linked list item that is used to implement a queue for the BFS
+ * scan.
+ * */
+struct fcs_states_linked_list_item_struct
+{
+ fcs_state_with_locations_t * s;
+ struct fcs_states_linked_list_item_struct * next;
+};
+
+typedef struct fcs_states_linked_list_item_struct fcs_states_linked_list_item_t;
+
+/*
+ * Conventions for use of the tests' order flags:
+ * A test that should be scanned sequentially should have both flags cleared.
+ * The first test in its random group should have both flags set. All the
+ * other tests in the group should contain the FLAG_RANDOM flag.
+ *
+ * For instance: 123(45)(67)8 translates into:
+ * 1 , 2, 3, 4|RANDOM|START_RANDOM_GROUP, 5|RANDOM,
+ * 6|RANDOM_START_RANDOM_GROUP, 7|RANDOM, 8
+ *
+ * */
+enum FCS_TESTS_ORDER_FLAGS
+{
+ FCS_TEST_ORDER_NO_FLAGS_MASK = 0xFFFFFF,
+ FCS_TEST_ORDER_FLAG_RANDOM = 0x1000000,
+ FCS_TEST_ORDER_FLAG_START_RANDOM_GROUP = 0x2000000
+};
+
+#ifdef FCS_WITH_TALONS
+#define FCS_TESTS_NUM 27
+#else
+#define FCS_TESTS_NUM 25
+#endif
+
+/*
+ * Declare these structures because they will be used within
+ * freecell_solver_instance, and they will contain a pointer to it.
+ * */
+struct freecell_solver_hard_thread_struct;
+struct freecell_solver_soft_thread_struct;
+
+typedef struct freecell_solver_hard_thread_struct freecell_solver_hard_thread_t;
+
+struct fcs_tests_order_struct
+{
+ int num;
+ int * tests;
+ int max_num;
+};
+
+typedef struct fcs_tests_order_struct fcs_tests_order_t;
+
+typedef struct freecell_solver_instance
+{
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT)
+ /* The sort-margin */
+ fcs_state_with_locations_t * indirect_prev_states_margin[PREV_STATES_SORT_MARGIN];
+
+ /* The number of states in the sort margin */
+ int num_prev_states_margin;
+
+ /* The sorted cached states, their number and their maximal size.
+ * max_num_indirect_prev_states may increase as the
+ * indirect_prev_states is realloced.
+ * */
+ fcs_state_with_locations_t * * indirect_prev_states;
+ int num_indirect_prev_states;
+ int max_num_indirect_prev_states;
+#endif
+
+ /* The number of states that were checked by the solving algorithm.
+ * Badly named, should be renamed to num_iters or num_checked_states */
+ int num_times;
+
+ /*
+ * A move stack that contains the moves leading to the solution.
+ *
+ * It is created only after the solution was found by swallowing
+ * all the stacks of each depth.
+ * */
+ fcs_move_stack_t * solution_moves;
+
+ /*
+ * Limits for the maximal depth and for the maximal number of checked
+ * states. max_num_times is useful because it enables the process to
+ * stop before it consumes too much memory.
+ *
+ * max_depth is quite dangerous because it blocks some intermediate moves
+ * and doesn't allow a program to fully reach its solution.
+ *
+ * */
+ int max_depth;
+ int max_num_times;
+
+ /*
+ * The debug_iter_output variables provide a programmer programmable way
+ * to debug the algorithm while it is running. This works well for DFS
+ * and Soft-DFS scans but at present support for A* and BFS is not
+ * too good, as its hard to tell which state came from which parent state.
+ *
+ * debug_iter_output is a flag that indicates whether to use this feature
+ * at all.
+ *
+ * debug_iter_output_func is a pointer to the function that performs the
+ * debugging.
+ *
+ * debug_iter_output_context is a user-specified context for it, that
+ * may include data that is not included in the instance structure.
+ *
+ * This feature is used by the "-s" and "-i" flags of fc-solve-debug.
+ * */
+ int debug_iter_output;
+ void (*debug_iter_output_func)(
+ void * context,
+ int iter_num,
+ int depth,
+ void * instance,
+ fcs_state_with_locations_t * state,
+ int parent_iter_num
+ );
+ void * debug_iter_output_context;
+
+ /*
+ * tree is the balanced binary tree that is used to store and index
+ * the checked states.
+ *
+ * */
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE)
+ struct rbtree * tree;
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE)
+ avl_tree * tree;
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE)
+ rb_tree * tree;
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_TREE)
+ GTree * tree;
+#endif
+
+ /*
+ * hash is the hash table that is used to store the previous
+ * states of the scan.
+ * */
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH)
+ GHashTable * hash;
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH)
+ SFO_hash_t * hash;
+#endif
+
+#if defined(INDIRECT_STACK_STATES)
+ /*
+ * The storage mechanism for the stacks assuming INDIRECT_STACK_STATES is
+ * used.
+ * */
+#if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH)
+ SFO_hash_t * stacks_hash;
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE)
+ avl_tree * stacks_tree;
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE)
+ rb_tree * stacks_tree;
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE)
+ struct rbtree * stacks_tree;
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE)
+ GTree * stacks_tree;
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH)
+ GHashTable * stacks_hash;
+#endif
+#endif
+
+ /*
+ * Storing using Berkeley DB is not operational for some reason so
+ * pay no attention to it for the while
+ * */
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_DB_FILE)
+ DB * db;
+#endif
+
+ /*
+ * The number of Freecells, Stacks and Foundations present in the game.
+ *
+ * freecells_num and stacks_num are variable and may be specified at
+ * the beginning of the execution of the algorithm. However, there
+ * is a maximal limit to them which is set in config.h.
+ *
+ * decks_num can be 4 or 8
+ * */
+ int freecells_num;
+ int stacks_num;
+ int decks_num;
+
+ /* What two adjacent cards in the same sequence can be: */
+ int sequences_are_built_by;
+ /* Whether an entire sequence can be moved from one place to the
+ * other regardless of the number of unoccupied Freecells there are. */
+ int unlimited_sequence_move;
+ /*
+ * With what cards can empty stacks be filled with.
+ * */
+ int empty_stacks_fill;
+
+#ifdef FCS_WITH_TALONS
+ /*
+ * The talon for Gypsy-like games. Since only the position changes from
+ * state to state.
+ * We can keep it here.
+ *
+ * */
+ fcs_card_t * gypsy_talon;
+
+ /*
+ * The length of the Gypsy talon
+ * */
+ int gypsy_talon_len;
+
+ int talon_type;
+
+ /* The Klondike Talons' Cache */
+ SFO_hash_t * talons_hash;
+
+#endif
+
+ /* A flag that indicates whether to optimize the solution path
+ at the end of the scan */
+ int optimize_solution_path;
+
+ /* This is a place-holder for the initial state */
+ fcs_state_with_locations_t * state_copy_ptr;
+
+ /* This is the final state that the scan recommends to the
+ * interface
+ * */
+ fcs_state_with_locations_t * final_state;
+
+ /*
+ * This is the number of states in the state collection.
+ *
+ * It gives a rough estimate of the memory occupied by the instance.
+ * */
+ int num_states_in_collection;
+
+ /*
+ * A limit to the above.
+ * */
+ int max_num_states_in_collection;
+
+ int num_hard_threads;
+ struct freecell_solver_hard_thread_struct * * hard_threads;
+
+ /*
+ * The next ID to allocate for a soft-thread.
+ * */
+ int next_soft_thread_id;
+
+ /*
+ * A persistent counters that os used to iterate over the
+ * threads one by one
+ * */
+ int ht_idx;
+
+ /*
+ * This is the master tests order. It is used to initialize all
+ * the new Soft-Threads.
+ * */
+ fcs_tests_order_t instance_tests_order;
+
+ /*
+ * This is the hard-thread used for the optimization scan.
+ * */
+ struct freecell_solver_hard_thread_struct * optimization_thread;
+
+ /*
+ * A counter that determines how many of the hard threads that belong
+ * to this hard thread have already finished. If it becomes num_hard_threads
+ * the instance terminates.
+ * */
+ int num_hard_threads_finished;
+
+ /*
+ * A flag that indicates whether or not to explicitly calculate
+ * the depth of a state that was reached.
+ * */
+ int calc_real_depth;
+
+ /*
+ * The tests order for the optimization scan as specified by the user.
+ * */
+ int opt_tests_order_set;
+
+ fcs_tests_order_t opt_tests_order;
+
+ /*
+ * This flag indicates whether scans should or should not reparent the
+ * states their encounter to a lower depth in the depth tree
+ * */
+ int to_reparent_states;
+
+ /*
+ * This variable determines how the scans cooperate with each other.
+ *
+ * A value of 0 indicates that they don't and only share the same
+ * states collection.
+ *
+ * A value of 1 indicates that they mark states as dead-end,
+ * which may help or hinder other scans.
+ * */
+ int scans_synergy;
+
+} freecell_solver_instance_t;
+
+
+
+
+/***************************************************/
+
+
+struct fcs_prelude_item_struct
+{
+ int scan_idx;
+ int quota;
+};
+
+typedef struct fcs_prelude_item_struct fcs_prelude_item_t;
+
+
+struct freecell_solver_hard_thread_struct
+{
+ freecell_solver_instance_t * instance;
+
+ int num_soft_threads;
+ struct freecell_solver_soft_thread_struct * * soft_threads;
+
+ /*
+ * The State Packs variables are used by all the state cache
+ * management routines. A pack stores as many states as can fit
+ * in a 64KB segment, and those variables manage an array of
+ * such packs.
+ *
+ * Such allocation is possible, because at the worst situation
+ * the last state is released.
+ * */
+ fcs_state_with_locations_t * * state_packs;
+ int max_num_state_packs;
+ int num_state_packs;
+ int num_states_in_last_pack;
+ int state_pack_len;
+
+ /*
+ * The hard thread count of how many states he checked himself. The
+ * instance num_times can be confusing because other threads modify it too.
+ *
+ * Thus, the soft thread switching should be done based on this variable
+ * */
+ int num_times;
+
+ /*
+ * The maximal limit for this variable.
+ * */
+ int max_num_times;
+
+ /*
+ * The Hard-Thread's global limit for the number of iterations
+ * to process
+ * */
+ int ht_max_num_times;
+
+ int num_times_step;
+
+ /*
+ * This is the number of iterations that still have to be done for
+ * soft_threads[st_idx]. It is reset to (st_idx+1)->num_times_step
+ * when st_idx is incremented.
+ * */
+ int num_times_left_for_soft_thread;
+
+ /*
+ * These variables are used to compute the MD5 checksum of a state
+ * that is about to be checked. I decided to make them globals so
+ * they won't have to be re-allocated and freed all the time.
+ *
+ * Notice that it is only used with my internal hash implementation
+ * as GLib requires a dedicated hash function, which cannot
+ * access the instance.
+ *
+ * */
+
+ /*
+ * The index for the soft-thread that is currently processed
+ * */
+ int st_idx;
+
+ /*
+ * A counter that determines how many of the soft threads that belong
+ * to this hard thread have already finished. If it becomes num_soft_threads
+ * this thread is skipped.
+ * */
+ int num_soft_threads_finished;
+
+#ifdef INDIRECT_STACK_STATES
+ /*
+ * This is the mechanism used to allocate memory for the stacks.
+ * */
+ fcs_compact_allocator_t * stacks_allocator;
+#endif
+
+ /*
+ * This is a compact memory allocator for the move stacks associated
+ * with the states in the states collection.
+ * */
+ fcs_compact_allocator_t * move_stacks_allocator;
+
+ /*
+ * This is a move stack that is used and re-used by the
+ * tests functions of this hard thread
+ * */
+ fcs_move_stack_t * reusable_move_stack;
+
+#ifdef INDIRECT_STACK_STATES
+ /*
+ * This is a buffer used to temporarily store the stacks of the duplicated
+ * state.
+ * */
+ fcs_card_t indirect_stacks_buffer[MAX_NUM_STACKS << 7];
+#else
+ fcs_card_t indirect_stacks_buffer[1];
+#endif
+
+ char * prelude_as_string;
+
+ int prelude_num_items;
+ int prelude_idx;
+ fcs_prelude_item_t * prelude;
+
+};
+
+
+
+
+
+/********************************************/
+
+
+
+
+
+
+
+
+struct fcs_soft_dfs_stack_item_struct
+{
+ fcs_state_with_locations_t * state;
+ fcs_derived_states_list_t derived_states_list;
+ int current_state_index;
+ int test_index;
+ int num_freestacks;
+ int num_freecells;
+ int derived_states_random_indexes_max_size;
+ int * derived_states_random_indexes;
+};
+
+typedef struct fcs_soft_dfs_stack_item_struct fcs_soft_dfs_stack_item_t;
+
+struct freecell_solver_soft_thread_struct
+{
+ freecell_solver_hard_thread_t * hard_thread;
+
+ /*
+ * The ID of the soft thread inside the instance.
+ * Used for the state-specific flags.
+ * */
+ int id;
+
+ /*
+ * The tests' order indicates which tests (i.e: kinds of multi-moves) to
+ * do at what order. This is most relevant to DFS and Soft-DFS.
+ *
+ * tests_order_num is the number of tests in the test's order. Notice
+ * that it can be lower than FCS_TESTS_NUM, thus enabling several tests
+ * to be removed completely.
+ * */
+ fcs_tests_order_t tests_order;
+
+
+ /*
+ * The (temporary) max depth of the Soft-DFS scans)
+ * */
+ int dfs_max_depth;
+ /*
+ * The method (i.e: DFS, Soft-DFS, BFS or A*) that is used by this
+ * instance.
+ *
+ * */
+ int method;
+
+ /*
+ * A place-holder for the original method of the scan in case
+ * it is replaced by FCS_METHOD_OPTIMIZE
+ *
+ * */
+ int orig_method;
+
+ /*
+ * A linked list that serves as the queue for the BFS scan.
+ * */
+ fcs_states_linked_list_item_t * bfs_queue;
+ /*
+ * The last item in the linked list, so new items can be added at it,
+ * thus making it a queue.
+ * */
+ fcs_states_linked_list_item_t * bfs_queue_last_item;
+
+ /*
+ * The priority queue of the A* scan */
+ PQUEUE * a_star_pqueue;
+ double a_star_initial_cards_under_sequences;
+
+ /*
+ * The A* weights of the different A* tests. Those weights determine the
+ * commulative weight of the state.
+ *
+ * */
+ double a_star_weights[5];
+
+ /*
+ * The first state to be checked by the scan. It is a kind of bootstrap
+ * for the algorithm.
+ * */
+ fcs_state_with_locations_t * first_state_to_check;
+
+ /*
+ * These are stacks used by the Soft-DFS for various uses.
+ *
+ * states_to_check[i] - an array of states to be checked next. Not all
+ * of them will be checked because it is possible that future states
+ * already visited them.
+ *
+ * states_to_check_move_stacks[i] - an array of move stacks that lead
+ * to those states.
+ *
+ * num_states_to_check[i] - the size of states_to_check[i]
+ *
+ * max_num_states_to_check[i] - the limit of pointers that can be
+ * placed in states_to_check[i] without resizing.
+ *
+ * current_state_indexes[i] - the index of the last checked state
+ * in depth i.
+ *
+ * test_indexes[i] - the index of the test that was last performed.
+ * FCS performs each test separately, so states_to_check[i] and
+ * friends will not be overpopulated.
+ *
+ * num_freestacks[i] - the number of unoccpied stacks that correspond
+ * to solution_states[i].
+ *
+ * num_freecells[i] - ditto for the freecells.
+ *
+ * */
+
+ fcs_soft_dfs_stack_item_t * soft_dfs_info;
+
+ /* The depth of the DFS stacks */
+ int num_solution_states;
+
+ /*
+ * A pseudo-random number generator for use in the random-DFS scan
+ * */
+ fcs_rand_t * rand_gen;
+
+ /*
+ * The initial seed of this random number generator
+ * */
+ unsigned int rand_seed;
+
+
+ /*
+ * A flag that indicates if this soft thread have already been
+ * initialized.
+ * */
+ int initialized;
+
+ /*
+ * The number of iterations with which to process this scan
+ * */
+ int num_times_step;
+
+ /*
+ * A flag that indicates if this scan contains all the tests that
+ * are accessible to all the other scans
+ * */
+ int is_a_complete_scan;
+
+ /*
+ * A flag that indicates if this scan has completed a scan. Used by
+ * solve_instance() to skip to the next scan.
+ * */
+ int is_finished;
+
+ /*
+ * A malloced string that serves as an identification for the user.
+ * */
+ char * name;
+};
+
+typedef struct freecell_solver_soft_thread_struct freecell_solver_soft_thread_t;
+
+
+#define FCS_SOFT_DFS_STATES_TO_CHECK_GROW_BY 32
+
+/*
+ * An enum that specifies the meaning of each A* weight.
+ * */
+#define FCS_A_STAR_WEIGHT_CARDS_OUT 0
+#define FCS_A_STAR_WEIGHT_MAX_SEQUENCE_MOVE 1
+#define FCS_A_STAR_WEIGHT_CARDS_UNDER_SEQUENCES 2
+#define FCS_A_STAR_WEIGHT_SEQS_OVER_RENEGADE_CARDS 3
+#define FCS_A_STAR_WEIGHT_DEPTH 4
+
+freecell_solver_instance_t * freecell_solver_alloc_instance(void);
+
+extern void freecell_solver_init_instance(
+ freecell_solver_instance_t * instance
+ );
+
+extern void freecell_solver_free_instance(
+ freecell_solver_instance_t * instance
+ );
+
+extern void freecell_solver_finish_instance(
+ freecell_solver_instance_t * instance
+ );
+
+extern int freecell_solver_solve_instance(
+ freecell_solver_instance_t * instance,
+ fcs_state_with_locations_t * init_state
+ );
+
+extern int freecell_solver_resume_instance(
+ freecell_solver_instance_t * instance
+ );
+
+extern void freecell_solver_unresume_instance(
+ freecell_solver_instance_t * instance
+ );
+
+extern freecell_solver_soft_thread_t * freecell_solver_instance_get_soft_thread(
+ freecell_solver_instance_t * instance,
+ int ht_idx,
+ int st_idx
+ );
+
+extern freecell_solver_soft_thread_t * freecell_solver_new_soft_thread(
+ freecell_solver_soft_thread_t * soft_thread
+ );
+
+extern freecell_solver_soft_thread_t * freecell_solver_new_hard_thread(
+ freecell_solver_instance_t * instance
+ );
+
+extern int freecell_solver_hard_dfs_solve_for_state(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int depth,
+ int ignore_osins
+ );
+
+extern int freecell_solver_soft_dfs_solve(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations_orig
+ );
+
+extern int freecell_solver_random_dfs_solve(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations_orig
+ );
+
+
+extern void freecell_solver_a_star_initialize_rater(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations
+ );
+
+extern int freecell_solver_a_star_or_bfs_do_solve_or_resume(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations_orig,
+ int resume
+ );
+
+extern int freecell_solver_hard_dfs_resume_solution(
+ freecell_solver_soft_thread_t * soft_thread,
+ int depth
+ );
+
+extern int freecell_solver_soft_dfs_resume_solution(
+ freecell_solver_soft_thread_t * soft_thread
+ );
+
+extern int freecell_solver_random_dfs_resume_solution(
+ freecell_solver_soft_thread_t * soft_thread
+ );
+
+
+extern int freecell_solver_a_star_or_bfs_solve(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations_orig
+ );
+
+extern int freecell_solver_a_star_or_bfs_resume_solution(
+ freecell_solver_soft_thread_t * soft_thread
+ );
+
+extern int freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations_orig,
+ int resume,
+ int to_randomize
+ );
+
+extern void freecell_solver_recycle_instance(
+ freecell_solver_instance_t * instance
+ );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FC_SOLVE__FCS_H */
diff --git a/kpat/freecell-solver/fcs_cl.h b/kpat/freecell-solver/fcs_cl.h
new file mode 100644
index 00000000..e739c98e
--- /dev/null
+++ b/kpat/freecell-solver/fcs_cl.h
@@ -0,0 +1,65 @@
+
+#ifndef FC_SOLVE__FCS_CL_H
+#define FC_SOLVE__FCS_CL_H
+
+#include "fcs_user.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*freecell_solver_user_cmd_line_known_commands_callback_t)
+ (
+ void * instance,
+ int argc,
+ char * argv[],
+ int arg_index,
+ int * num_to_skip,
+ int * ret,
+ void * context
+ );
+
+enum FCS_CMD_LINE_CALLBACK_RET_VALUES
+{
+ FCS_CMD_LINE_OK,
+ FCS_CMD_LINE_SKIP,
+ FCS_CMD_LINE_STOP,
+ FCS_CMD_LINE_UNRECOGNIZED_OPTION,
+ FCS_CMD_LINE_PARAM_WITH_NO_ARG,
+ FCS_CMD_LINE_ERROR_IN_ARG,
+
+ FCS_CMD_LINE_USER = 0x10000
+};
+
+extern int freecell_solver_user_cmd_line_parse_args(
+ void * instance,
+ int argc,
+ const char * argv[],
+ int start_arg,
+ char * * known_parameters,
+ freecell_solver_user_cmd_line_known_commands_callback_t callback,
+ void * callback_context,
+ char * * error_string,
+ int * last_arg
+ );
+
+extern int freecell_solver_user_cmd_line_parse_args_with_file_nesting_count(
+ void * instance,
+ int argc,
+ const char * argv[],
+ int start_arg,
+ char * * known_parameters,
+ freecell_solver_user_cmd_line_known_commands_callback_t callback,
+ void * callback_context,
+ char * * error_string,
+ int * last_arg,
+ int file_nesting_count,
+ char * opened_files_dir
+ );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef FC_SOLVE__FCS_CL_H */
diff --git a/kpat/freecell-solver/fcs_config.h b/kpat/freecell-solver/fcs_config.h
new file mode 100644
index 00000000..8a25205d
--- /dev/null
+++ b/kpat/freecell-solver/fcs_config.h
@@ -0,0 +1,95 @@
+/* config.h. Generated by configure. */
+/* config.h.in. Generated from configure.ac by autoheader. */
+/*
+ config.h - Configuration file for Freecell Solver
+
+ Written by Shlomi Fish, 2000
+
+ This file is distributed under the public domain.
+ (It is not copyrighted).
+*/
+
+#ifndef FC_SOLVE__CONFIG_H
+#define FC_SOLVE__CONFIG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* #undef DEBUG_STATES */
+/* #undef COMPACT_STATES */
+#define INDIRECT_STACK_STATES 1
+
+/* #undef CARD_DEBUG_PRES */
+
+/*
+ * Define this macro if the C compiler supports the keyword inline or
+ * a similar keyword that was found by Autoconf (and defined as inline).
+ * */
+#define HAVE_C_INLINE 1
+
+
+/*
+ The sort margin size for the previous states array.
+*/
+#define PREV_STATES_SORT_MARGIN 32
+/*
+ The amount prev_states grow by each time it each resized.
+ Should be greater than 0 and in order for the program to be
+ efficient, should be much bigger than
+ PREV_STATES_SORT_MARGIN.
+*/
+#define PREV_STATES_GROW_BY 128
+
+/*
+ The amount the pack pointers array grows by. Shouldn't be too high
+ because it doesn't happen too often.
+*/
+#define IA_STATE_PACKS_GROW_BY 32
+
+/*
+ * The maximal number of Freecells. For efficiency's sake it should be a
+ * multiple of 4.
+ * */
+
+#define MAX_NUM_FREECELLS 4
+
+/*
+ * The maximal number of Stacks. For efficiency's sake it should be a
+ * multiple of 4.
+ * */
+
+#define MAX_NUM_STACKS 10
+/*
+ * The maximal number of initial cards that can be found in a stack.
+ * */
+#define MAX_NUM_INITIAL_CARDS_IN_A_STACK 8
+
+#define MAX_NUM_DECKS 2
+
+
+#define FCS_STATE_STORAGE_INDIRECT 0
+#define FCS_STATE_STORAGE_INTERNAL_HASH 1
+#define FCS_STATE_STORAGE_LIBAVL_AVL_TREE 2
+#define FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE 3
+#define FCS_STATE_STORAGE_LIBREDBLACK_TREE 4
+#define FCS_STATE_STORAGE_GLIB_TREE 5
+#define FCS_STATE_STORAGE_GLIB_HASH 6
+#define FCS_STATE_STORAGE_DB_FILE 7
+
+#define FCS_STACK_STORAGE_INTERNAL_HASH 0
+#define FCS_STACK_STORAGE_LIBAVL_AVL_TREE 1
+#define FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE 2
+#define FCS_STACK_STORAGE_LIBREDBLACK_TREE 3
+#define FCS_STACK_STORAGE_GLIB_TREE 4
+#define FCS_STACK_STORAGE_GLIB_HASH 5
+
+#define FCS_STATE_STORAGE FCS_STATE_STORAGE_INTERNAL_HASH
+#define FCS_STACK_STORAGE FCS_STACK_STORAGE_INTERNAL_HASH
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/kpat/freecell-solver/fcs_dm.c b/kpat/freecell-solver/fcs_dm.c
new file mode 100644
index 00000000..9fd8c9a8
--- /dev/null
+++ b/kpat/freecell-solver/fcs_dm.c
@@ -0,0 +1,146 @@
+/*
+ fcs_dm.c - Freecell Solver's data management routines.
+
+ Written by Shlomi Fish, 2000
+
+ This file is distributed under the public domain.
+ (It's not copyrighted)
+*/
+
+#include <stddef.h>
+#include <string.h>
+
+#include "fcs_dm.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+/*
+ freecell_solver_bsearch - an improved binary search function. Highlights:
+
+ * The comparison function accepts a common context argument that
+ is passed to SFO_bsearch.
+ * If the item was not found the function returns the place in which
+ it should be placed, while setting *found to 0. If it was found
+ (*found) is set to 1.
+*/
+void * freecell_solver_bsearch
+(
+ void * key,
+ void * void_array,
+ size_t len,
+ size_t width,
+ int (* compare)(const void *, const void *, void *),
+ void * context,
+ int * found
+)
+{
+ int low = 0;
+ int high = len-1;
+ int mid;
+ int result;
+
+ char * array = void_array;
+
+ while (low <= high)
+ {
+ mid = ((low+high)>>1);
+
+ result = compare(key, (void*)(array+mid*width), context);
+
+ if (result < 0)
+ {
+ high = mid-1;
+ }
+ else if (result > 0)
+ {
+ low = mid+1;
+ }
+ else
+ {
+ *found = 1;
+ return (void*)(array+mid*width);
+ }
+ }
+
+ *found = 0;
+ return ((void*)(array+(high+1)*width));
+}
+
+
+
+/*
+ freecell_solver_merge_large_and_small_sorted_array - merges a large sorted
+ array with a small sorted array. The arrays could be of any length
+ whatsoever, but it works faster if the first is significantly bigger
+ than the second.
+
+ This function assumes that big_array is allocated with enough
+ space to hold the extra elements.
+
+ The array should be distinct or else there would be unexpected
+ results.
+*/
+int freecell_solver_merge_large_and_small_sorted_arrays
+(
+ void * void_big_array,
+ size_t size_big_array,
+ void * void_small_array,
+ size_t size_small_array,
+ size_t width,
+ int (*compare) (const void *, const void *, void *),
+ void * context
+)
+{
+ int item_to_move, num_big_items_moved, pos;
+ char * pos_ptr;
+ char * big_array;
+ char * small_array;
+ int found;
+ int start_offset, end_offset;
+
+ big_array = (char*)void_big_array;
+ small_array = (char*)void_small_array;
+
+ num_big_items_moved = 0;
+
+ for(item_to_move = size_small_array-1 ; item_to_move>=0; item_to_move--)
+ {
+ pos_ptr = freecell_solver_bsearch (
+ small_array+item_to_move*width,
+ big_array,
+ size_big_array-num_big_items_moved,
+ width,
+ compare,
+ context,
+ &found
+ );
+
+ pos = (pos_ptr-big_array)/width;
+
+ end_offset = size_big_array + size_small_array -
+ num_big_items_moved -
+ (size_small_array-item_to_move-1);
+
+ start_offset = end_offset + pos -
+ (size_big_array - num_big_items_moved);
+
+ memmove(
+ big_array+start_offset*width,
+ big_array+pos*width,
+ (end_offset-start_offset)*width
+ );
+
+ memcpy(
+ big_array+(start_offset-1)*width,
+ small_array+item_to_move*width,
+ width
+ );
+
+ num_big_items_moved += (end_offset - start_offset);
+ }
+
+ return 1;
+}
+
diff --git a/kpat/freecell-solver/fcs_dm.h b/kpat/freecell-solver/fcs_dm.h
new file mode 100644
index 00000000..2cb6dc82
--- /dev/null
+++ b/kpat/freecell-solver/fcs_dm.h
@@ -0,0 +1,49 @@
+/*
+ fcs_dm.h - Header file for Freecell Solver's Data Management
+ routines.
+
+ For more information consult fcs_dm.c.
+
+ Written by Shlomi Fish, 2000
+ This file is distributed under the public domain.
+ (It is not copyrighted)
+*/
+
+#ifndef FC_SOLVE__FCS_DATA_H
+#define FC_SOLVE__FCS_DATA_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+
+void * freecell_solver_bsearch
+(
+ void * key,
+ void * void_array,
+ size_t len,
+ size_t width,
+ int (* compare)(const void *, const void *, void *),
+ void * context,
+ int * found
+);
+
+int freecell_solver_merge_large_and_small_sorted_arrays
+(
+ void * void_big_array,
+ size_t size_big_array,
+ void * void_small_array,
+ size_t size_small_array,
+ size_t width,
+ int (*compare) (const void *, const void *, void *),
+ void * context
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FC_SOLVE__FCS_DATA_H */
+
diff --git a/kpat/freecell-solver/fcs_enums.h b/kpat/freecell-solver/fcs_enums.h
new file mode 100644
index 00000000..071383c9
--- /dev/null
+++ b/kpat/freecell-solver/fcs_enums.h
@@ -0,0 +1,77 @@
+/*
+ * fcs_enums.h - header file for various Freecell Solver Enumertaions. Common
+ * to the main program headers and to the library headers.
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+
+#ifndef FC_SOLVE__FCS_ENUMS_H
+#define FC_SOLVE__FCS_ENUMS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum FCS_EMPTY_STACKS_FILL_T
+{
+ FCS_ES_FILLED_BY_ANY_CARD,
+ FCS_ES_FILLED_BY_KINGS_ONLY,
+ FCS_ES_FILLED_BY_NONE
+};
+
+enum FCS_SEQUENCES_ARE_BUILT_BY_T
+{
+ FCS_SEQ_BUILT_BY_ALTERNATE_COLOR,
+ FCS_SEQ_BUILT_BY_SUIT,
+ FCS_SEQ_BUILT_BY_RANK
+};
+
+enum FCS_TALON_T
+{
+ FCS_TALON_NONE,
+ FCS_TALON_GYPSY,
+ FCS_TALON_KLONDIKE
+};
+
+enum freecell_solver_state_solving_return_codes
+{
+ FCS_STATE_WAS_SOLVED,
+ FCS_STATE_IS_NOT_SOLVEABLE,
+ FCS_STATE_ALREADY_EXISTS,
+ FCS_STATE_EXCEEDS_MAX_NUM_TIMES,
+ FCS_STATE_BEGIN_SUSPEND_PROCESS,
+ FCS_STATE_SUSPEND_PROCESS,
+ FCS_STATE_EXCEEDS_MAX_DEPTH,
+ FCS_STATE_ORIGINAL_STATE_IS_NOT_SOLVEABLE,
+ FCS_STATE_INVALID_STATE,
+ FCS_STATE_NOT_BEGAN_YET,
+ FCS_STATE_DOES_NOT_EXIST,
+ FCS_STATE_OPTIMIZED
+};
+
+enum fcs_presets_return_codes
+{
+ FCS_PRESET_CODE_OK,
+ FCS_PRESET_CODE_NOT_FOUND,
+ FCS_PRESET_CODE_FREECELLS_EXCEED_MAX,
+ FCS_PRESET_CODE_STACKS_EXCEED_MAX,
+ FCS_PRESET_CODE_DECKS_EXCEED_MAX
+};
+
+
+#define FCS_METHOD_NONE -1
+#define FCS_METHOD_HARD_DFS 0
+#define FCS_METHOD_SOFT_DFS 1
+#define FCS_METHOD_BFS 2
+#define FCS_METHOD_A_STAR 3
+#define FCS_METHOD_OPTIMIZE 4
+#define FCS_METHOD_RANDOM_DFS 5
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FC_SOLVE__FCS_ENUMS_H */
diff --git a/kpat/freecell-solver/fcs_hash.c b/kpat/freecell-solver/fcs_hash.c
new file mode 100644
index 00000000..fde7a03f
--- /dev/null
+++ b/kpat/freecell-solver/fcs_hash.c
@@ -0,0 +1,291 @@
+/*
+ * fcs_hash.c - an implementation of a simplistic (keys only) hash. This
+ * hash uses chaining and re-hashing and was found to be very fast. Not all
+ * of the functions of the hash ADT are implemented, but it is useful enough
+ * for Freecell Solver.
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+
+#include "fcs_config.h"
+
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) || (defined(INDIRECT_STACK_STATES) && (FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH))
+
+#include <stdlib.h>
+#include <string.h>
+
+#define DEBUG
+
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
+#include "fcs_hash.h"
+
+#include "alloc.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+static void SFO_hash_rehash(SFO_hash_t * hash);
+
+
+
+SFO_hash_t * freecell_solver_hash_init(
+ SFO_hash_value_t wanted_size,
+ int (*compare_function)(const void * key1, const void * key2, void * context),
+ void * context
+ )
+{
+ int size;
+ SFO_hash_t * hash;
+
+ /* Find a prime number that is greater than the initial wanted size */
+ size = 256;
+ while (size < wanted_size)
+ {
+ size <<= 1;
+ }
+
+ hash = (SFO_hash_t *)malloc(sizeof(SFO_hash_t));
+
+ hash->size = size;
+ hash->size_bitmask = size-1;
+
+ hash->num_elems = 0;
+
+ /* Allocate a table of size entries */
+ hash->entries = (SFO_hash_symlink_t *)malloc(
+ sizeof(SFO_hash_symlink_t) * size
+ );
+
+ hash->compare_function = compare_function;
+ hash->context = context;
+
+ /* Initialize all the cells of the hash table to NULL, which indicate
+ that the cork of the linked list is right at the start */
+ memset(hash->entries, 0, sizeof(SFO_hash_symlink_t)*size);
+
+ hash->allocator = freecell_solver_compact_allocator_new();
+
+ return hash;
+}
+
+void * freecell_solver_hash_insert(
+ SFO_hash_t * hash,
+ void * key,
+ SFO_hash_value_t hash_value,
+ SFO_hash_value_t secondary_hash_value,
+ int optimize_for_caching
+ )
+{
+ int place;
+ SFO_hash_symlink_t * list;
+ SFO_hash_symlink_item_t * item, * last_item;
+
+ /* Get the index of the appropriate chain in the hash table */
+ place = hash_value & (hash->size_bitmask);
+
+ list = &(hash->entries[place]);
+ /* If first_item is non-existent */
+ if (list->first_item == NULL)
+ {
+ /* Allocate a first item with that key */
+ fcs_compact_alloc_into_var(item, hash->allocator, SFO_hash_symlink_item_t);
+ list->first_item = item;
+ item->next = NULL;
+ item->key = key;
+ item->hash_value = hash_value;
+ item->secondary_hash_value = secondary_hash_value;
+
+ goto rehash_check;
+ }
+
+ /* Initialize item to the chain's first_item */
+ item = list->first_item;
+ last_item = NULL;
+
+ while (item != NULL)
+ {
+ /*
+ We first compare the hash values, because it is faster than
+ comparing the entire data structure.
+
+ */
+ if (
+ (item->hash_value == hash_value) &&
+ (item->secondary_hash_value == secondary_hash_value) &&
+ (!(hash->compare_function(item->key, key, hash->context)))
+ )
+ {
+ if (optimize_for_caching)
+ {
+ /*
+ * Place the item in the beginning of the chain.
+ * If last_item == NULL it is already the first item so leave
+ * it alone
+ * */
+ if (last_item != NULL)
+ {
+ last_item->next = item->next;
+ item->next = list->first_item;
+ list->first_item = item;
+ }
+ }
+ return item->key;
+ }
+ /* Cache the item before the current in last_item */
+ last_item = item;
+ /* Move to the next item */
+ item = item->next;
+ }
+
+ if (optimize_for_caching)
+ {
+ /* Put the new element at the beginning of the list */
+ fcs_compact_alloc_into_var(item, hash->allocator, SFO_hash_symlink_item_t);
+ item->next = list->first_item;
+ item->key = key;
+ item->hash_value = hash_value;
+ list->first_item = item;
+ item->secondary_hash_value = secondary_hash_value;
+ }
+ else
+ {
+ /* Put the new element at the end of the list */
+ fcs_compact_alloc_into_var(item, hash->allocator, SFO_hash_symlink_item_t);
+ last_item->next = item;
+ item->next = NULL;
+ item->key = key;
+ item->hash_value = hash_value;
+ item->secondary_hash_value = secondary_hash_value;
+ }
+
+rehash_check:
+
+ hash->num_elems++;
+
+ if (hash->num_elems > ((hash->size*3)>>2))
+ {
+ SFO_hash_rehash(hash);
+ }
+
+ return NULL;
+}
+
+void freecell_solver_hash_free_with_callback(
+ SFO_hash_t * hash,
+ void (*function_ptr)(void * key, void * context)
+ )
+{
+ int i;
+ SFO_hash_symlink_item_t * item, * next_item;
+
+ for(i=0;i<hash->size;i++)
+ {
+ item = hash->entries[i].first_item;
+ while (item != NULL)
+ {
+ function_ptr(item->key, hash->context);
+ next_item = item->next;
+
+ item = next_item;
+ }
+ }
+
+ freecell_solver_hash_free(hash);
+}
+
+void freecell_solver_hash_free(
+ SFO_hash_t * hash
+ )
+{
+ freecell_solver_compact_allocator_finish(hash->allocator);
+
+ free(hash->entries);
+
+ free(hash);
+}
+
+
+/*
+ This function "rehashes" a hash. I.e: it increases the size of its
+ hash table, allowing for smaller chains, and faster lookup.
+
+ */
+static void SFO_hash_rehash(
+ SFO_hash_t * hash
+ )
+{
+ int old_size, new_size, new_size_bitmask;
+ int i;
+#if 0
+ SFO_hash_t * new_hash;
+#endif
+ SFO_hash_symlink_item_t * item, * next_item;
+ int place;
+ SFO_hash_symlink_t * new_entries;
+
+ old_size = hash->size;
+
+#if 0
+ /* Allocate a new hash with hash_init() */
+ new_hash = freecell_solver_hash_init_proto(
+ old_size * 2,
+ hash->compare_function,
+ hash->context
+ );
+#endif
+
+ old_size = hash->size;
+ new_size = old_size << 1;
+ new_size_bitmask = new_size - 1;
+
+ new_entries = calloc(new_size, sizeof(SFO_hash_symlink_t));
+
+ /* Copy the items to the new hash while not allocating them again */
+ for(i=0;i<old_size;i++)
+ {
+ item = hash->entries[i].first_item;
+ /* traverse the chain item by item */
+ while(item != NULL)
+ {
+ /* The place in the new hash table */
+ place = item->hash_value & new_size_bitmask;
+
+ /* Store the next item in the linked list in a safe place,
+ so we can retrieve it after the assignment */
+ next_item = item->next;
+ /* It is placed in front of the first element in the chain,
+ so it should link to it */
+ item->next = new_entries[place].first_item;
+
+ /* Make it the first item in its chain */
+ new_entries[place].first_item = item;
+
+ /* Move to the next item this one. */
+ item = next_item;
+ }
+ };
+
+ /* Free the entries of the old hash */
+ free(hash->entries);
+
+ /* Copy the new hash to the old one */
+#if 0
+ *hash = *new_hash;
+#endif
+ hash->entries = new_entries;
+ hash->size = new_size;
+ hash->size_bitmask = new_size_bitmask;
+}
+
+#else
+
+/* ANSI C doesn't allow empty compilation */
+static void freecell_solver_hash_c_dummy();
+
+#endif /* (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH) || defined(INDIRECT_STACK_STATES) */
diff --git a/kpat/freecell-solver/fcs_hash.h b/kpat/freecell-solver/fcs_hash.h
new file mode 100644
index 00000000..fbe6c78c
--- /dev/null
+++ b/kpat/freecell-solver/fcs_hash.h
@@ -0,0 +1,102 @@
+/*
+ * fcs_hash.h - header file of Freecell Solver's internal hash implementation.
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+
+#ifndef FC_SOLVE__FCS_HASH_H
+#define FC_SOLVE__FCS_HASH_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "alloc.h"
+#include "lookup2.h"
+
+typedef int SFO_hash_value_t;
+
+struct SFO_hash_symlink_item_struct
+{
+ /* A pointer to the data structure that is to be collected */
+ void * key;
+ /* We also store the hash value corresponding to this key for faster
+ comparisons */
+ SFO_hash_value_t hash_value;
+ /*
+ * We also store a secondary hash value, which is not used for indexing,
+ * but is used to speed up comparison.
+ * */
+ SFO_hash_value_t secondary_hash_value;
+ /* The next item in the list */
+ struct SFO_hash_symlink_item_struct * next;
+};
+
+typedef struct SFO_hash_symlink_item_struct SFO_hash_symlink_item_t;
+
+struct SFO_hash_symlink_struct
+{
+ SFO_hash_symlink_item_t * first_item;
+};
+
+typedef struct SFO_hash_symlink_struct SFO_hash_symlink_t;
+
+struct SFO_hash_struct
+{
+ /* The vector of the hash table itself */
+ SFO_hash_symlink_t * entries;
+ /* A comparison function that can be used for comparing two keys
+ in the collection */
+ int (*compare_function)(const void * key1, const void * key2, void * context);
+ /* The size of the hash table */
+ int size;
+
+ /* A bit mask that extract the lowest bits out of the hash value */
+ int size_bitmask;
+ /* The number of elements stored inside the hash */
+ int num_elems;
+ /* A context to pass to the comparison function */
+ void * context;
+
+ fcs_compact_allocator_t * allocator;
+};
+
+typedef struct SFO_hash_struct SFO_hash_t;
+
+
+SFO_hash_t * freecell_solver_hash_init(
+ SFO_hash_value_t wanted_size,
+ int (*compare_function)(const void * key1, const void * key2, void * context),
+ void * context
+ );
+
+void * freecell_solver_hash_insert(
+ SFO_hash_t * hash,
+ void * key,
+ SFO_hash_value_t hash_value,
+ SFO_hash_value_t secondary_hash_value,
+ int optimize_for_caching
+ );
+
+
+void freecell_solver_hash_free(
+ SFO_hash_t * hash
+ );
+
+void freecell_solver_hash_free_with_callback(
+ SFO_hash_t * hash,
+ void (*function_ptr)(void * key, void * context)
+ );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FC_SOLVE__FCS_HASH_H */
+
+
+
+
diff --git a/kpat/freecell-solver/fcs_isa.c b/kpat/freecell-solver/fcs_isa.c
new file mode 100644
index 00000000..0a6ffe51
--- /dev/null
+++ b/kpat/freecell-solver/fcs_isa.c
@@ -0,0 +1,88 @@
+/* fcs_isa.c - Freecell Solver Indirect State Allocation Routines
+
+ Written by Shlomi Fish, 2000
+ This file is distributed under the public domain.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "fcs_config.h"
+
+
+#include "state.h"
+#include "fcs.h"
+
+#include "fcs_isa.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+void freecell_solver_state_ia_init(freecell_solver_hard_thread_t * hard_thread)
+{
+ hard_thread->max_num_state_packs = IA_STATE_PACKS_GROW_BY;
+ hard_thread->state_packs = (fcs_state_with_locations_t * *)malloc(sizeof(fcs_state_with_locations_t *) * hard_thread->max_num_state_packs);
+ hard_thread->num_state_packs = 1;
+ /*
+ * All the states should fit in one 64KB segment. Now, we allocate as
+ * many states as possible, minus one, so we would be certain that there
+ * would be place for the overhead required by the malloc algorithm.
+ * */
+ hard_thread->state_pack_len = (0x010000 / sizeof(fcs_state_with_locations_t)) - 1;
+ hard_thread->state_packs[0] = malloc(hard_thread->state_pack_len*sizeof(fcs_state_with_locations_t));
+
+ hard_thread->num_states_in_last_pack = 0;
+}
+
+#if 0
+fcs_state_with_locations_t * fcs_state_ia_alloc(freecell_solver_hard_thread_t * hard_thread)
+{
+ if (hard_thread->num_states_in_last_pack == hard_thread->state_pack_len)
+ {
+ if (hard_thread->num_state_packs == hard_thread->max_num_state_packs)
+ {
+ hard_thread->max_num_state_packs += IA_STATE_PACKS_GROW_BY;
+ hard_thread->state_packs = (fcs_state_with_locations_t * *)realloc(hard_thread->state_packs, sizeof(fcs_state_with_locations_t *) * hard_thread->max_num_state_packs);
+ }
+ hard_thread->state_packs[hard_thread->num_state_packs] = malloc(hard_thread->state_pack_len * sizeof(fcs_state_with_locations_t));
+ hard_thread->num_state_packs++;
+ hard_thread->num_states_in_last_pack = 0;
+ }
+ return &(hard_thread->state_packs[hard_thread->num_state_packs-1][hard_thread->num_states_in_last_pack++]);
+}
+#endif
+
+#if 0
+void fcs_state_ia_release(freecell_solver_hard_thread_t * hard_thread)
+{
+ hard_thread->num_states_in_last_pack--;
+}
+#endif
+
+void freecell_solver_state_ia_finish(freecell_solver_hard_thread_t * hard_thread)
+{
+ int a;
+ for(a=0;a<hard_thread->num_state_packs;a++)
+ {
+ free(hard_thread->state_packs[a]);
+ }
+ free(hard_thread->state_packs);
+ hard_thread->state_packs = NULL;
+}
+
+void freecell_solver_state_ia_foreach(freecell_solver_hard_thread_t * hard_thread, void (*ptr_function)(fcs_state_with_locations_t *, void *), void * context)
+{
+ int p,s;
+ for(p=0;p<hard_thread->num_state_packs-1;p++)
+ {
+ for(s=0 ; s < hard_thread->state_pack_len ; s++)
+ {
+ ptr_function(&(hard_thread->state_packs[p][s]), context);
+ }
+ }
+ for(s=0; s < hard_thread->num_states_in_last_pack ; s++)
+ {
+ ptr_function(&(hard_thread->state_packs[p][s]), context);
+ }
+}
diff --git a/kpat/freecell-solver/fcs_isa.h b/kpat/freecell-solver/fcs_isa.h
new file mode 100644
index 00000000..30a9a982
--- /dev/null
+++ b/kpat/freecell-solver/fcs_isa.h
@@ -0,0 +1,56 @@
+#ifndef FC_SOLVE__FCS_ISA_H
+#define FC_SOLVE__FCS_ISA_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "state.h"
+#include "fcs.h"
+
+extern void freecell_solver_state_ia_init(freecell_solver_hard_thread_t * hard_thread);
+#if 0
+extern fcs_state_with_locations_t * fcs_state_ia_alloc(freecell_solver_instance_t * instance);
+#else
+
+#define fcs_state_ia_alloc_into_var(ret, instance) \
+{ \
+ if ((instance)->num_states_in_last_pack == (instance)->state_pack_len) \
+ { \
+ if (instance->num_state_packs == instance->max_num_state_packs) \
+ { \
+ instance->max_num_state_packs += IA_STATE_PACKS_GROW_BY; \
+ instance->state_packs = (fcs_state_with_locations_t * *)realloc(instance->state_packs, sizeof(fcs_state_with_locations_t *) * instance->max_num_state_packs); \
+ } \
+ instance->state_packs[instance->num_state_packs] = malloc(instance->state_pack_len * sizeof(fcs_state_with_locations_t)); \
+ instance->num_state_packs++; \
+ instance->num_states_in_last_pack = 0; \
+ } \
+ ret = &(instance->state_packs[instance->num_state_packs-1][instance->num_states_in_last_pack++]); \
+}
+
+#endif
+
+
+#if 0
+extern void fcs_state_ia_release(freecell_solver_instance_t * instance);
+#else
+#define fcs_state_ia_release(instance) \
+{ \
+ (instance)->num_states_in_last_pack--; \
+}
+
+
+#endif
+extern void freecell_solver_state_ia_finish(freecell_solver_hard_thread_t * hard_thread);
+
+extern void freecell_solver_state_ia_foreach(
+ freecell_solver_hard_thread_t * hard_thread,
+ void (*ptr_function)(fcs_state_with_locations_t *, void *),
+ void * context
+ );
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/kpat/freecell-solver/fcs_move.h b/kpat/freecell-solver/fcs_move.h
new file mode 100644
index 00000000..ecb5166f
--- /dev/null
+++ b/kpat/freecell-solver/fcs_move.h
@@ -0,0 +1,122 @@
+/*
+ * fcs_move.h - header file for the move structure and enums of
+ * Freecell Solver. This file is common to the main code and to the
+ * library headers.
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+
+#ifndef FC_SOLVE__FCS_MOVE_H
+#define FC_SOLVE__FCS_MOVE_H
+
+/* #define FCS_DEBUG_MOVES */
+#define FCS_COMPACT_MOVES
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum fcs_move_types
+{
+ FCS_MOVE_TYPE_STACK_TO_STACK,
+ FCS_MOVE_TYPE_STACK_TO_FREECELL,
+ FCS_MOVE_TYPE_FREECELL_TO_STACK,
+ FCS_MOVE_TYPE_FREECELL_TO_FREECELL,
+ FCS_MOVE_TYPE_STACK_TO_FOUNDATION,
+ FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION,
+ FCS_MOVE_TYPE_FLIP_CARD,
+ FCS_MOVE_TYPE_DEAL_GYPSY_TALON,
+ FCS_MOVE_TYPE_KLONDIKE_TALON_TO_STACK,
+ FCS_MOVE_TYPE_KLONDIKE_FLIP_TALON,
+ FCS_MOVE_TYPE_KLONDIKE_REDEAL_TALON,
+ FCS_MOVE_TYPE_SEQ_TO_FOUNDATION,
+ FCS_MOVE_TYPE_CANONIZE,
+ FCS_MOVE_TYPE_SEPARATOR,
+ FCS_MOVE_TYPE_NULL
+};
+
+#ifdef FCS_DEBUG_MOVES
+struct fcs_move_struct
+{
+ /* The index of the foundation, in case there are more than one decks */
+ int foundation;
+ /* Used in the case of a stack to stack move */
+ int num_cards_in_sequence;
+ /* There are two freecells, one for the source and the other
+ * for the destination */
+ int src_freecell;
+ int dest_freecell;
+ /* Ditto for the stacks */
+ int src_stack;
+ int dest_stack;
+ /* The type of the move see the enum fcs_move_types */
+ int type;
+};
+
+#define fcs_move_set_src_stack(move,value) (move).src_stack = (value);
+#define fcs_move_set_src_freecell(move,value) (move).src_freecell = (value);
+#define fcs_move_set_dest_stack(move,value) (move).dest_stack = (value);
+#define fcs_move_set_dest_freecell(move,value) (move).dest_freecell = (value);
+#define fcs_move_set_foundation(move,value) (move).foundation = (value);
+#define fcs_move_set_type(move,value) (move).type = (value);
+#define fcs_move_set_num_cards_in_seq(move,value) (move).num_cards_in_sequence = (value);
+
+#define fcs_move_get_src_stack(move) ((move).src_stack)
+#define fcs_move_get_src_freecell(move) ((move).src_freecell)
+#define fcs_move_get_dest_stack(move) ((move).dest_stack)
+#define fcs_move_get_dest_freecell(move) ((move).dest_freecell)
+#define fcs_move_get_foundation(move) ((move).foundation)
+#define fcs_move_get_type(move) ((move).type)
+#define fcs_move_get_num_cards_in_seq(move) ((move).num_cards_in_sequence)
+
+#elif defined(FCS_COMPACT_MOVES)
+struct fcs_move_struct
+{
+ unsigned char c[4];
+};
+
+#define FCS_MOVE_TYPE 0
+#define FCS_MOVE_SRC 1
+#define FCS_MOVE_DEST 2
+#define FCS_MOVE_NUM_CARDS_IN_SEQ 3
+#define FCS_MOVE_NUM_CARDS_FLIPPED 3
+
+#define fcs_move_set_src_stack(move,value) (move).c[FCS_MOVE_SRC] = (value);
+#define fcs_move_set_src_freecell(move,value) (move).c[FCS_MOVE_SRC] = (value);
+#define fcs_move_set_dest_stack(move,value) (move).c[FCS_MOVE_DEST] = (value);
+#define fcs_move_set_dest_freecell(move,value) (move).c[FCS_MOVE_DEST] = (value);
+#define fcs_move_set_foundation(move,value) (move).c[FCS_MOVE_DEST] = (value);
+#define fcs_move_set_type(move,value) (move).c[FCS_MOVE_TYPE] = (value);
+#define fcs_move_set_num_cards_in_seq(move,value) (move).c[FCS_MOVE_NUM_CARDS_IN_SEQ] = (value);
+#define fcs_move_set_num_cards_flipped(move,value) (move).c[FCS_MOVE_NUM_CARDS_FLIPPED] = (value);
+
+#define fcs_move_get_src_stack(move) ((move).c[FCS_MOVE_SRC])
+#define fcs_move_get_src_freecell(move) ((move).c[FCS_MOVE_SRC])
+#define fcs_move_get_dest_stack(move) ((move).c[FCS_MOVE_DEST])
+#define fcs_move_get_dest_freecell(move) ((move).c[FCS_MOVE_DEST])
+#define fcs_move_get_foundation(move) ((move).c[FCS_MOVE_DEST])
+#define fcs_move_get_type(move) ((move).c[FCS_MOVE_TYPE])
+#define fcs_move_get_num_cards_in_seq(move) ((move).c[FCS_MOVE_NUM_CARDS_IN_SEQ])
+#define fcs_move_get_num_cards_flipped(move,value) ((move).c[FCS_MOVE_NUM_CARDS_FLIPPED])
+#define fcs_move_init(move) (memset((move).c, 0, 4))
+#endif
+
+typedef struct fcs_move_struct fcs_move_t;
+
+struct fcs_move_stack_struct
+{
+ fcs_move_t * moves;
+ int max_num_moves;
+ int num_moves;
+};
+
+typedef struct fcs_move_stack_struct fcs_move_stack_t;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FC_SOLVE__FCS_MOVE_H */
diff --git a/kpat/freecell-solver/fcs_user.h b/kpat/freecell-solver/fcs_user.h
new file mode 100644
index 00000000..8ddbf6f2
--- /dev/null
+++ b/kpat/freecell-solver/fcs_user.h
@@ -0,0 +1,275 @@
+/*
+ * move.h - main header file for the Freecell Solver library.
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+#ifndef FC_SOLVE__FCS_USER_H
+#define FC_SOLVE__FCS_USER_H
+
+#include "fcs_enums.h"
+#include "fcs_move.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+extern void * freecell_solver_user_alloc(void);
+
+extern int freecell_solver_user_apply_preset(
+ void * instance,
+ const char * preset_name
+ );
+
+extern void freecell_solver_user_limit_iterations(
+ void * user_instance,
+ int max_iters
+ );
+
+extern int freecell_solver_user_set_tests_order(
+ void * user_instance,
+ const char * tests_order,
+ char * * error_string
+ );
+
+extern int freecell_solver_user_solve_board(
+ void * user_instance,
+ const char * state_as_string
+ );
+
+extern int freecell_solver_user_resume_solution(
+ void * user_instance
+ );
+
+extern int freecell_solver_user_get_next_move(
+ void * user_instance,
+ fcs_move_t * move
+ );
+
+extern char * freecell_solver_user_current_state_as_string(
+ void * user_instance,
+ int parseable_output,
+ int canonized_order_output,
+ int display_10_as_t
+ );
+
+extern void freecell_solver_user_free(
+ void * user_instance
+ );
+
+extern int freecell_solver_user_get_current_depth(
+ void * user_instance
+ );
+
+extern void freecell_solver_user_set_solving_method(
+ void * user_instance,
+ int method
+ );
+
+extern int freecell_solver_user_get_num_times(
+ void * user_instance
+ );
+
+extern int freecell_solver_user_get_limit_iterations(
+ void * user_instance
+ );
+
+extern int freecell_solver_user_get_moves_left(
+ void * user_instance
+ );
+
+extern int freecell_solver_user_set_game(
+ void * user_instance,
+ int freecells_num,
+ int stacks_num,
+ int decks_num,
+ int sequences_are_built_by,
+ int unlimited_sequence_move,
+ int empty_stacks_fill
+ );
+
+extern void freecell_solver_user_set_solution_optimization(
+ void * user_instance,
+ int optimize
+);
+
+extern char * freecell_solver_user_move_to_string(
+ fcs_move_t move,
+ int standard_notation
+ );
+
+extern char * freecell_solver_user_move_to_string_w_state(
+ void * user_instance,
+ fcs_move_t move,
+ int standard_notation
+ );
+
+extern void freecell_solver_user_limit_depth(
+ void * user_instance,
+ int max_depth
+ );
+
+extern int freecell_solver_user_set_num_freecells(
+ void * user_instance,
+ int freecells_num
+ );
+
+extern int freecell_solver_user_get_max_num_freecells(void);
+
+extern int freecell_solver_user_set_num_stacks(
+ void * user_instance,
+ int stacks_num
+ );
+
+extern int freecell_solver_user_get_max_num_stacks(void);
+
+extern int freecell_solver_user_set_num_decks(
+ void * user_instance,
+ int decks_num
+ );
+
+extern int freecell_solver_user_get_max_num_decks(void);
+
+
+extern char * freecell_solver_user_get_invalid_state_error_string(
+ void * user_instance,
+ int print_ts
+ );
+
+extern int freecell_solver_user_set_sequences_are_built_by_type(
+ void * user_instance,
+ int sbb
+ );
+
+extern int freecell_solver_user_set_empty_stacks_filled_by(
+ void * user_instance,
+ int es_fill
+ );
+
+extern int freecell_solver_user_set_sequence_move(
+ void * user_instance,
+ int unlimited
+ );
+
+extern int freecell_solver_user_set_a_star_weight(
+ void * user_instance,
+ int index,
+ double weight
+ );
+
+typedef void (*freecell_solver_user_iter_handler_t)
+ (
+ void * user_instance,
+ int iter_num,
+ int depth,
+ void * ptr_state,
+ int parent_iter_num,
+ void * context
+ );
+
+extern void freecell_solver_user_set_iter_handler(
+ void * user_instance,
+ freecell_solver_user_iter_handler_t iter_handler,
+ void * iter_handler_context
+ );
+
+
+extern char * freecell_solver_user_iter_state_as_string(
+ void * user_instance,
+ void * ptr_state,
+ int parseable_output,
+ int canonized_order_output,
+ int display_10_as_t
+ );
+
+extern void freecell_solver_user_set_random_seed(
+ void * user_instance,
+ int seed
+ );
+
+extern int freecell_solver_user_get_num_states_in_collection(
+ void * user_instance
+ );
+
+extern void freecell_solver_user_limit_num_states_in_collection(
+ void * user_instance,
+ int max_num_states
+ );
+
+extern int freecell_solver_user_next_soft_thread(
+ void * user_instance
+ );
+
+extern void freecell_solver_user_set_soft_thread_step(
+ void * user_instance,
+ int num_times_step
+ );
+
+extern int freecell_solver_user_next_hard_thread(
+ void * user_instance
+ );
+
+extern int freecell_solver_user_get_num_soft_threads_in_instance(
+ void * user_instance
+ );
+
+extern void freecell_solver_user_set_calc_real_depth(
+ void * user_instance,
+ int calc_real_depth
+ );
+
+extern void freecell_solver_user_set_soft_thread_name(
+ void * user_instance,
+ char * name
+ );
+
+extern int freecell_solver_user_set_hard_thread_prelude(
+ void * user_instance,
+ char * prelude
+ );
+
+extern void freecell_solver_user_recycle(
+ void * user_instance
+ );
+
+extern int freecell_solver_user_set_optimization_scan_tests_order(
+ void * user_instance,
+ const char * tests_order,
+ char * * error_string
+ );
+
+extern void freecell_solver_user_set_reparent_states(
+ void * user_instance,
+ int to_reparent_states
+ );
+
+extern void freecell_solver_user_set_scans_synergy(
+ void * user_instance,
+ int synergy
+ );
+
+extern void freecell_solver_user_limit_current_instance_iterations(
+ void * user_instance,
+ int max_iters
+ );
+
+extern int freecell_solver_user_next_instance(
+ void * user_instance
+ );
+
+/*
+ * This function resets the user_instance, making it lose
+ * all the previous command line arguments it encountered
+ * */
+extern int freecell_solver_user_reset(
+ void * user_instance
+ );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FC_SOLVE__FCS_USER_H */
diff --git a/kpat/freecell-solver/freecell.c b/kpat/freecell-solver/freecell.c
new file mode 100644
index 00000000..159772ff
--- /dev/null
+++ b/kpat/freecell-solver/freecell.c
@@ -0,0 +1,2433 @@
+/*
+ * freecell.c - The various movement tests performed by Freecell Solver
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000-2001
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <limits.h>
+
+
+#include "fcs_config.h"
+
+#if FCS_STATE_STORAGE==FCS_STATE_STORAGE_LIBREDBLACK_TREE
+#include <search.h>
+#endif
+
+#include "state.h"
+#include "card.h"
+#include "fcs_dm.h"
+#include "fcs.h"
+
+#include "fcs_isa.h"
+#include "tests.h"
+#include "ms_ca.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+#define state_with_locations (*ptr_state_with_locations)
+#define state (ptr_state_with_locations->s)
+#define new_state_with_locations (*ptr_new_state_with_locations)
+#define new_state (ptr_new_state_with_locations->s)
+
+
+#ifndef min
+#define min(a,b) (((a)<(b))?(a):(b))
+#endif
+
+/*
+ * Throughout this code the following local variables are used to quickly
+ * access the instance's members:
+ *
+ * state_stacks_num - the number of stacks in the state
+ * state_freecells_num - the number of freecells in the state
+ * sequences_are_built_by - the type of sequences of this board.
+ * */
+
+/*
+ * This function tries to move stack cards that are present at the
+ * top of stacks to the foundations.
+ * */
+int freecell_solver_sfs_move_top_stack_cards_to_founds(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+ int stack;
+ int cards_num;
+ int deck;
+ fcs_card_t card;
+ fcs_card_t temp_card;
+ int check;
+ int state_stacks_num;
+
+ fcs_move_t temp_move;
+
+ tests_define_accessors();
+
+ moves = hard_thread->reusable_move_stack;
+ indirect_stacks_buffer = hard_thread->indirect_stacks_buffer;
+
+ state_stacks_num = instance->stacks_num;
+
+ for(stack=0;stack<state_stacks_num;stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+ if (cards_num)
+ {
+ /* Get the top card in the stack */
+ card = fcs_stack_card(state,stack,cards_num-1);
+ for(deck=0;deck<instance->decks_num;deck++)
+ {
+ if (fcs_foundation_value(state, deck*4+fcs_card_suit(card)) == fcs_card_card_num(card) - 1)
+ {
+ /* We can put it there */
+
+ sfs_check_state_begin();
+
+
+ my_copy_stack(stack);
+ fcs_pop_stack_card(new_state, stack, temp_card);
+
+ fcs_increment_foundation(new_state, deck*4+fcs_card_suit(card));
+
+
+ fcs_move_init(temp_move);
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FOUNDATION);
+ fcs_move_set_src_stack(temp_move,stack);
+ fcs_move_set_foundation(temp_move,deck*4+fcs_card_suit(card));
+
+ fcs_move_stack_push(moves, temp_move);
+
+ fcs_flip_top_card(stack);
+
+ /* The last move needs to be FCS_MOVE_TYPE_CANONIZE
+ * because it indicates that the internal order of the
+ * stacks
+ * and freecells may have changed. */
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE);
+ fcs_move_stack_push(moves, temp_move);
+
+ sfs_check_state_end()
+ break;
+ }
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+
+/*
+ * This test moves single cards that are present in the freecells to
+ * the foundations.
+ * */
+int freecell_solver_sfs_move_freecell_cards_to_founds(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+ int fc;
+ int deck;
+ fcs_card_t card;
+ int check;
+ fcs_move_t temp_move;
+ int state_freecells_num;
+
+ tests_define_accessors();
+
+ state_freecells_num = instance->freecells_num;
+
+ /* Now check the same for the free cells */
+ for(fc=0;fc<state_freecells_num;fc++)
+ {
+ card = fcs_freecell_card(state, fc);
+ if (fcs_card_card_num(card) != 0)
+ {
+ for(deck=0;deck<instance->decks_num;deck++)
+ {
+ if (fcs_foundation_value(state, deck*4+fcs_card_suit(card)) == fcs_card_card_num(card) - 1)
+ {
+ /* We can put it there */
+ sfs_check_state_begin()
+
+ fcs_empty_freecell(new_state, fc);
+
+ fcs_increment_foundation(new_state, deck*4+fcs_card_suit(card));
+
+ fcs_move_init(temp_move);
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION);
+ fcs_move_set_src_freecell(temp_move,fc);
+ fcs_move_set_foundation(temp_move,deck*4+fcs_card_suit(card));
+
+ fcs_move_stack_push(moves, temp_move);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE);
+ fcs_move_stack_push(moves, temp_move);
+
+ sfs_check_state_end();
+ }
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+int freecell_solver_sfs_move_freecell_cards_on_top_of_stacks(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+
+ int dest_cards_num;
+ int ds, fc, dc;
+ fcs_card_t dest_card, src_card, temp_card, dest_below_card;
+ int check;
+
+ fcs_move_t temp_move;
+ int is_seq_in_dest;
+ int num_cards_to_relocate;
+ int freecells_to_fill, freestacks_to_fill;
+ int a,b;
+ int state_freecells_num, state_stacks_num, sequences_are_built_by;
+
+ tests_define_accessors();
+
+ state_freecells_num = instance->freecells_num;
+ state_stacks_num = instance->stacks_num;
+ sequences_are_built_by = instance->sequences_are_built_by;
+
+ /* Let's try to put cards in the freecells on top of stacks */
+
+ /* ds stands for destination stack */
+ for(ds=0;ds<state_stacks_num;ds++)
+ {
+ dest_cards_num = fcs_stack_len(state, ds);
+
+ /* If the stack is not empty we can proceed */
+ if (dest_cards_num > 0)
+ {
+ /*
+ * Let's search for a suitable card in the stack
+ * */
+ for(dc=dest_cards_num-1;dc>=0;dc--)
+ {
+ dest_card = fcs_stack_card(state, ds, dc);
+
+ /* Scan the freecells */
+ for(fc=0;fc<state_freecells_num;fc++)
+ {
+ src_card = fcs_freecell_card(state, fc);
+
+ /* If the freecell is not empty and dest_card is its parent
+ * */
+ if ( (fcs_card_card_num(src_card) != 0) &&
+ fcs_is_parent_card(src_card,dest_card) )
+ {
+ /* Let's check if we can put it there */
+
+ /* Check if the destination card is already below a
+ * suitable card */
+ is_seq_in_dest = 0;
+ if (dest_cards_num - 1 > dc)
+ {
+ dest_below_card = fcs_stack_card(state, ds, dc+1);
+ if (fcs_is_parent_card(dest_below_card, dest_card))
+ {
+ is_seq_in_dest = 1;
+ }
+ }
+
+
+ if (! is_seq_in_dest)
+ {
+ num_cards_to_relocate = dest_cards_num - dc - 1;
+
+ freecells_to_fill = min(num_cards_to_relocate, num_freecells);
+
+ num_cards_to_relocate -= freecells_to_fill;
+
+ if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD)
+ {
+ freestacks_to_fill = min(num_cards_to_relocate, num_freestacks);
+
+ num_cards_to_relocate -= freestacks_to_fill;
+ }
+ else
+ {
+ freestacks_to_fill = 0;
+ }
+
+ if (num_cards_to_relocate == 0)
+ {
+ /* We can move it */
+
+ sfs_check_state_begin()
+
+
+ /* Fill the freecells with the top cards */
+
+ my_copy_stack(ds);
+
+ for(a=0 ; a<freecells_to_fill ; a++)
+ {
+ /* Find a vacant freecell */
+ for(b=0;b<state_freecells_num;b++)
+ {
+ if (fcs_freecell_card_num(new_state, b) == 0)
+ {
+ break;
+ }
+ }
+ fcs_pop_stack_card(new_state, ds, temp_card);
+
+ fcs_put_card_in_freecell(new_state, b, temp_card);
+
+ fcs_move_init(temp_move);
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FREECELL);
+ fcs_move_set_src_stack(temp_move,ds);
+ fcs_move_set_dest_freecell(temp_move,b);
+ fcs_move_stack_push(moves, temp_move);
+ }
+
+ /* Fill the free stacks with the cards below them */
+ for(a=0; a < freestacks_to_fill ; a++)
+ {
+ /* Find a vacant stack */
+ for(b=0;b<state_stacks_num;b++)
+ {
+ if (fcs_stack_len(new_state, b) == 0)
+ {
+ break;
+ }
+ }
+ my_copy_stack(b);
+
+ fcs_pop_stack_card(new_state, ds, temp_card);
+ fcs_push_card_into_stack(new_state, b, temp_card);
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
+ fcs_move_set_src_stack(temp_move,ds);
+ fcs_move_set_dest_stack(temp_move,b);
+ fcs_move_set_num_cards_in_seq(temp_move,1);
+ fcs_move_stack_push(moves, temp_move);
+ }
+
+ /* Now put the freecell card on top of the stack */
+
+ fcs_push_card_into_stack(new_state, ds, src_card);
+ fcs_empty_freecell(new_state, fc);
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_FREECELL_TO_STACK);
+ fcs_move_set_src_freecell(temp_move,fc);
+ fcs_move_set_dest_stack(temp_move,ds);
+ fcs_move_stack_push(moves, temp_move);
+
+ sfs_check_state_end()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+
+
+int freecell_solver_sfs_move_non_top_stack_cards_to_founds(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+
+ int check;
+
+ int stack;
+ int cards_num;
+ int c, a, b;
+ fcs_card_t temp_card, card;
+ int deck;
+ int state_freecells_num;
+ int state_stacks_num;
+
+ fcs_move_t temp_move;
+ fcs_move_init(temp_move);
+
+ tests_define_accessors();
+
+ state_freecells_num = instance->freecells_num;
+ state_stacks_num = instance->stacks_num;
+
+
+
+ /* Now let's check if a card that is under some other cards can be placed
+ * in the foundations. */
+
+ for(stack=0;stack<state_stacks_num;stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+ /*
+ * We starts from cards_num-2 because the top card is already covered
+ * by move_top_stack_cards_to_founds.
+ * */
+ for(c=cards_num-2 ; c >= 0 ; c--)
+ {
+ card = fcs_stack_card(state, stack, c);
+ for(deck=0;deck<instance->decks_num;deck++)
+ {
+ if (fcs_foundation_value(state, deck*4+fcs_card_suit(card)) == fcs_card_card_num(card)-1)
+ {
+ /* The card is foundation-able. Now let's check if we
+ * can move the cards above it to the freecells and
+ * stacks */
+
+ if ((num_freecells +
+ ((instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) ?
+ num_freestacks :
+ 0
+ ))
+ >= cards_num-(c+1))
+ {
+ /* We can move it */
+
+ sfs_check_state_begin()
+
+ my_copy_stack(stack);
+
+
+ /* Fill the freecells with the top cards */
+ for(a=0 ; a<min(num_freecells, cards_num-(c+1)) ; a++)
+ {
+ /* Find a vacant freecell */
+ for(b=0; b<state_freecells_num; b++)
+ {
+ if (fcs_freecell_card_num(new_state, b) == 0)
+ {
+ break;
+ }
+ }
+ fcs_pop_stack_card(new_state, stack, temp_card);
+
+ fcs_put_card_in_freecell(new_state, b, temp_card);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FREECELL);
+ fcs_move_set_src_stack(temp_move,stack);
+ fcs_move_set_dest_freecell(temp_move,b);
+
+ fcs_move_stack_push(moves, temp_move);
+
+ fcs_flip_top_card(stack);
+ }
+
+ /* Fill the free stacks with the cards below them */
+ for(a=0; a < cards_num-(c+1) - min(num_freecells, cards_num-(c+1)) ; a++)
+ {
+ /* Find a vacant stack */
+ for(b=0;b<state_stacks_num;b++)
+ {
+ if (fcs_stack_len(new_state, b) == 0)
+ {
+ break;
+ }
+ }
+
+ my_copy_stack(b);
+
+ fcs_pop_stack_card(new_state, stack, temp_card);
+ fcs_push_card_into_stack(new_state, b, temp_card);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
+ fcs_move_set_src_stack(temp_move,stack);
+ fcs_move_set_dest_stack(temp_move,b);
+ fcs_move_set_num_cards_in_seq(temp_move,1);
+
+ fcs_move_stack_push(moves, temp_move);
+
+ fcs_flip_top_card(stack);
+ }
+
+ fcs_pop_stack_card(new_state, stack, temp_card);
+ fcs_increment_foundation(new_state, deck*4+fcs_card_suit(temp_card));
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FOUNDATION);
+ fcs_move_set_src_stack(temp_move,stack);
+ fcs_move_set_foundation(temp_move,deck*4+fcs_card_suit(temp_card));
+
+ fcs_move_stack_push(moves, temp_move);
+
+ fcs_flip_top_card(stack);
+
+ sfs_check_state_end()
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+
+
+
+int freecell_solver_sfs_move_stack_cards_to_a_parent_on_the_same_stack(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+ int check;
+
+ int stack, c, cards_num, a, dc,b;
+ int is_seq_in_dest;
+ fcs_card_t card, temp_card, prev_card;
+ fcs_card_t dest_below_card, dest_card;
+ int freecells_to_fill, freestacks_to_fill;
+ int dest_cards_num, num_cards_to_relocate;
+ int state_freecells_num;
+ int state_stacks_num;
+ int sequences_are_built_by;
+
+ fcs_move_t temp_move;
+ fcs_move_init(temp_move);
+
+ tests_define_accessors();
+
+ state_freecells_num = instance->freecells_num;
+ state_stacks_num = instance->stacks_num;
+ sequences_are_built_by = instance->sequences_are_built_by;
+
+ /*
+ * Now let's try to move a stack card to a parent card which is found
+ * on the same stack.
+ * */
+ for (stack=0;stack<state_stacks_num;stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+
+ for (c=0 ; c<cards_num ; c++)
+ {
+ /* Find a card which this card can be put on; */
+
+ card = fcs_stack_card(state, stack, c);
+
+
+ /* Do not move cards that are already found above a suitable parent */
+ a = 1;
+ if (c != 0)
+ {
+ prev_card = fcs_stack_card(state, stack, c-1);
+ if ((fcs_card_card_num(prev_card) == fcs_card_card_num(card)+1) &&
+ ((fcs_card_suit(prev_card) & 0x1) != (fcs_card_suit(card) & 0x1)))
+ {
+ a = 0;
+ }
+ }
+ if (a)
+ {
+#define ds stack
+ /* Check if it can be moved to something on the same stack */
+ dest_cards_num = fcs_stack_len(state, ds);
+ for(dc=0;dc<c-1;dc++)
+ {
+ dest_card = fcs_stack_card(state, ds, dc);
+ if (fcs_is_parent_card(card, dest_card))
+ {
+ /* Corresponding cards - see if it is feasible to move
+ the source to the destination. */
+
+ is_seq_in_dest = 0;
+ dest_below_card = fcs_stack_card(state, ds, dc+1);
+ if (fcs_is_parent_card(dest_below_card, dest_card))
+ {
+ is_seq_in_dest = 1;
+ }
+
+ if (!is_seq_in_dest)
+ {
+ num_cards_to_relocate = dest_cards_num - dc - 1;
+
+ freecells_to_fill = min(num_cards_to_relocate, num_freecells);
+
+ num_cards_to_relocate -= freecells_to_fill;
+
+ if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD)
+ {
+ freestacks_to_fill = min(num_cards_to_relocate, num_freestacks);
+
+ num_cards_to_relocate -= freestacks_to_fill;
+ }
+ else
+ {
+ freestacks_to_fill = 0;
+ }
+
+ if (num_cards_to_relocate == 0)
+ {
+ /* We can move it */
+
+ sfs_check_state_begin()
+
+
+ {
+ int i_card_pos;
+ fcs_card_t moved_card;
+ int source_type, source_index;
+
+ i_card_pos = fcs_stack_len(new_state,stack)-1;
+ a = 0;
+
+ my_copy_stack(ds);
+ while(i_card_pos>c)
+ {
+ if (a < freecells_to_fill)
+ {
+ for(b=0;b<state_freecells_num;b++)
+ {
+ if (fcs_freecell_card_num(new_state, b) == 0)
+ {
+ break;
+ }
+ }
+ fcs_pop_stack_card(new_state, ds, temp_card);
+ fcs_put_card_in_freecell(new_state, b, temp_card);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FREECELL);
+ fcs_move_set_src_stack(temp_move,ds);
+ fcs_move_set_dest_freecell(temp_move,b);
+
+ fcs_move_stack_push(moves, temp_move);
+
+ }
+ else
+ {
+
+ /* Find a vacant stack */
+ for(b=0;b<state_stacks_num;b++)
+ {
+ if (fcs_stack_len(new_state, b) == 0)
+ {
+ break;
+ }
+ }
+
+ my_copy_stack(b);
+
+ fcs_pop_stack_card(new_state, ds, temp_card);
+ fcs_push_card_into_stack(new_state, b, temp_card);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
+ fcs_move_set_src_stack(temp_move,ds);
+ fcs_move_set_dest_stack(temp_move,b);
+ fcs_move_set_num_cards_in_seq(temp_move,1);
+
+ fcs_move_stack_push(moves, temp_move);
+
+ }
+ a++;
+
+ i_card_pos--;
+ }
+ fcs_pop_stack_card(new_state, ds, moved_card);
+ if (a < freecells_to_fill)
+ {
+ for(b=0;b<state_freecells_num;b++)
+ {
+ if (fcs_freecell_card_num(new_state, b) == 0)
+ {
+ break;
+ }
+ }
+ fcs_put_card_in_freecell(new_state, b, moved_card);
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FREECELL);
+ fcs_move_set_src_stack(temp_move,ds);
+ fcs_move_set_dest_freecell(temp_move,b);
+ fcs_move_stack_push(moves, temp_move);
+
+ source_type = 0;
+ source_index = b;
+ }
+ else
+ {
+ for(b=0;b<state_stacks_num;b++)
+ {
+ if (fcs_stack_len(new_state, b) == 0)
+ {
+ break;
+ }
+ }
+
+ my_copy_stack(b);
+ fcs_push_card_into_stack(new_state, b, moved_card);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
+ fcs_move_set_src_stack(temp_move,ds);
+ fcs_move_set_dest_stack(temp_move,b);
+ fcs_move_set_num_cards_in_seq(temp_move,1);
+ fcs_move_stack_push(moves, temp_move);
+
+ source_type = 1;
+ source_index = b;
+ }
+ i_card_pos--;
+ a++;
+
+ while(i_card_pos>dc)
+ {
+ if (a < freecells_to_fill)
+ {
+ for(b=0;b<state_freecells_num;b++)
+ {
+ if (fcs_freecell_card_num(new_state, b) == 0)
+ {
+ break;
+ }
+ }
+ fcs_pop_stack_card(new_state, ds, temp_card);
+ fcs_put_card_in_freecell(new_state, b, temp_card);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FREECELL);
+ fcs_move_set_src_stack(temp_move,ds);
+ fcs_move_set_dest_freecell(temp_move,b);
+
+ fcs_move_stack_push(moves, temp_move);
+ }
+ else
+ {
+
+ /* Find a vacant stack */
+ for(b=0;b<state_stacks_num;b++)
+ {
+ if (fcs_stack_len(new_state, b) == 0)
+ {
+ break;
+ }
+ }
+
+ fcs_pop_stack_card(new_state, ds, temp_card);
+ my_copy_stack(b);
+ fcs_push_card_into_stack(new_state, b, temp_card);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
+ fcs_move_set_src_stack(temp_move,ds);
+ fcs_move_set_dest_stack(temp_move,b);
+ fcs_move_set_num_cards_in_seq(temp_move,1);
+
+ fcs_move_stack_push(moves, temp_move);
+
+ }
+ a++;
+
+ i_card_pos--;
+ }
+
+ if (source_type == 0)
+ {
+ moved_card = fcs_freecell_card(new_state, source_index);
+ fcs_empty_freecell(new_state, source_index);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_FREECELL_TO_STACK);
+ fcs_move_set_src_freecell(temp_move,source_index);
+ fcs_move_set_dest_stack(temp_move,ds);
+ fcs_move_stack_push(moves, temp_move);
+ }
+ else
+ {
+ my_copy_stack(source_index);
+ fcs_pop_stack_card(new_state, source_index, moved_card);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
+ fcs_move_set_src_stack(temp_move,source_index);
+ fcs_move_set_dest_stack(temp_move,ds);
+ fcs_move_set_num_cards_in_seq(temp_move,1);
+ fcs_move_stack_push(moves, temp_move);
+ }
+
+ fcs_push_card_into_stack(new_state, ds, moved_card);
+ }
+
+ sfs_check_state_end()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+#undef ds
+
+
+int freecell_solver_sfs_move_stack_cards_to_different_stacks(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+
+ int check;
+
+ int stack, c, cards_num, a, dc, ds,b;
+ int is_seq_in_dest;
+ fcs_card_t card, temp_card, this_card, prev_card;
+ fcs_card_t dest_below_card, dest_card;
+ int freecells_to_fill, freestacks_to_fill;
+ int dest_cards_num, num_cards_to_relocate;
+ int seq_end;
+ int state_freecells_num;
+ int state_stacks_num;
+ int sequences_are_built_by;
+
+ fcs_move_t temp_move;
+ fcs_move_init(temp_move);
+
+ tests_define_accessors();
+
+ state_freecells_num = instance->freecells_num;
+ state_stacks_num = instance->stacks_num;
+ sequences_are_built_by = instance->sequences_are_built_by;
+
+ /* Now let's try to move a card from one stack to the other *
+ * Note that it does not involve moving cards lower than king *
+ * to empty stacks */
+
+ for (stack=0;stack<state_stacks_num;stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+
+ for (c=0 ; c<cards_num ; c=seq_end+1)
+ {
+ /* Check if there is a sequence here. */
+ for(seq_end=c ; seq_end<cards_num-1 ; seq_end++)
+ {
+ this_card = fcs_stack_card(state, stack, seq_end+1);
+ prev_card = fcs_stack_card(state, stack, seq_end);
+
+ if (fcs_is_parent_card(this_card,prev_card))
+ {
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ /* Find a card which this card can be put on; */
+
+ card = fcs_stack_card(state, stack, c);
+
+ /* Make sure the card is not flipped or else we can't move it */
+ if (fcs_card_get_flipped(card) == 0)
+ {
+ for(ds=0 ; ds<state_stacks_num; ds++)
+ {
+ if (ds != stack)
+ {
+ dest_cards_num = fcs_stack_len(state, ds);
+ for(dc=0;dc<dest_cards_num;dc++)
+ {
+ dest_card = fcs_stack_card(state, ds, dc);
+
+ if (fcs_is_parent_card(card, dest_card))
+ {
+ /* Corresponding cards - see if it is feasible to move
+ the source to the destination. */
+
+ is_seq_in_dest = 0;
+ if (dest_cards_num - 1 > dc)
+ {
+ dest_below_card = fcs_stack_card(state, ds, dc+1);
+ if (fcs_is_parent_card(dest_below_card, dest_card))
+ {
+ is_seq_in_dest = 1;
+ }
+ }
+
+ if (! is_seq_in_dest)
+ {
+ num_cards_to_relocate = dest_cards_num - dc - 1 + cards_num - seq_end - 1;
+
+ freecells_to_fill = min(num_cards_to_relocate, num_freecells);
+
+ num_cards_to_relocate -= freecells_to_fill;
+
+ if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD)
+ {
+ freestacks_to_fill = min(num_cards_to_relocate, num_freestacks);
+
+ num_cards_to_relocate -= freestacks_to_fill;
+ }
+ else
+ {
+ freestacks_to_fill = 0;
+ }
+
+ if ((num_cards_to_relocate == 0) &&
+ (calc_max_sequence_move(num_freecells-freecells_to_fill, num_freestacks-freestacks_to_fill) >=
+ seq_end - c + 1))
+ {
+ /* We can move it */
+ int from_which_stack;
+
+ sfs_check_state_begin()
+
+
+ /* Fill the freecells with the top cards */
+
+ my_copy_stack(stack);
+ my_copy_stack(ds);
+
+ for(a=0 ; a<freecells_to_fill ; a++)
+ {
+ /* Find a vacant freecell */
+ for(b=0;b<state_freecells_num;b++)
+ {
+ if (fcs_freecell_card_num(new_state, b) == 0)
+ {
+ break;
+ }
+ }
+
+ if (fcs_stack_len(new_state, ds) == dc+1)
+ {
+ from_which_stack = stack;
+ }
+ else
+ {
+ from_which_stack = ds;
+ }
+ my_copy_stack(from_which_stack);
+
+ fcs_pop_stack_card(new_state, from_which_stack, temp_card);
+
+ fcs_put_card_in_freecell(new_state, b, temp_card);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FREECELL);
+ fcs_move_set_src_stack(temp_move,from_which_stack);
+ fcs_move_set_dest_freecell(temp_move,b);
+ fcs_move_stack_push(moves, temp_move);
+
+ fcs_flip_top_card(from_which_stack);
+ }
+
+ /* Fill the free stacks with the cards below them */
+ for(a=0; a < freestacks_to_fill ; a++)
+ {
+ /* Find a vacant stack */
+ for(b=0;b<state_stacks_num;b++)
+ {
+ if (fcs_stack_len(new_state, b) == 0)
+ {
+ break;
+ }
+ }
+
+ if (fcs_stack_len(new_state, ds) == dc+1)
+ {
+ from_which_stack = stack;
+ }
+ else
+ {
+ from_which_stack = ds;
+ }
+
+ my_copy_stack(b);
+ fcs_pop_stack_card(new_state, from_which_stack, temp_card);
+ fcs_push_card_into_stack(new_state, b, temp_card);
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
+ fcs_move_set_src_stack(temp_move,from_which_stack);
+ fcs_move_set_dest_stack(temp_move,b);
+ fcs_move_set_num_cards_in_seq(temp_move,1);
+ fcs_move_stack_push(moves, temp_move);
+
+ fcs_flip_top_card(from_which_stack);
+ }
+
+ for(a=c ; a <= seq_end ; a++)
+ {
+ fcs_push_stack_card_into_stack(new_state, ds, stack, a);
+ }
+
+ for(a=0; a < seq_end-c+1 ;a++)
+ {
+ fcs_pop_stack_card(new_state, stack, temp_card);
+ }
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
+ fcs_move_set_src_stack(temp_move,stack);
+ fcs_move_set_dest_stack(temp_move,ds);
+ fcs_move_set_num_cards_in_seq(temp_move,seq_end-c+1);
+ fcs_move_stack_push(moves, temp_move);
+
+ fcs_flip_top_card(stack);
+
+ sfs_check_state_end()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+
+
+int freecell_solver_sfs_move_sequences_to_free_stacks(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+ int check;
+
+ int stack, cards_num, c, ds, a, b, seq_end;
+ fcs_card_t this_card, prev_card, temp_card;
+ int max_sequence_len;
+ int num_cards_to_relocate, freecells_to_fill, freestacks_to_fill;
+ int state_freecells_num;
+ int state_stacks_num;
+ int sequences_are_built_by;
+
+ fcs_move_t temp_move;
+ fcs_move_init(temp_move);
+
+ tests_define_accessors();
+
+ if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_NONE)
+ {
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+ }
+
+ state_freecells_num = instance->freecells_num;
+ state_stacks_num = instance->stacks_num;
+ sequences_are_built_by = instance->sequences_are_built_by;
+
+ max_sequence_len = calc_max_sequence_move(num_freecells, num_freestacks-1);
+
+ /* Now try to move sequences to empty stacks */
+
+ if (num_freestacks > 0)
+ {
+ for(stack=0;stack<state_stacks_num;stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+
+ for(c=0; c<cards_num; c=seq_end+1)
+ {
+ /* Check if there is a sequence here. */
+ for(seq_end=c ; seq_end<cards_num-1; seq_end++)
+ {
+ this_card = fcs_stack_card(state, stack, seq_end+1);
+ prev_card = fcs_stack_card(state, stack, seq_end);
+
+ if (! fcs_is_parent_card(this_card, prev_card))
+ {
+ break;
+ }
+ }
+
+ if ((fcs_stack_card_num(state, stack, c) != 13) &&
+ (instance->empty_stacks_fill == FCS_ES_FILLED_BY_KINGS_ONLY))
+ {
+ continue;
+ }
+
+ if (seq_end == cards_num -1)
+ {
+ /* One stack is the destination stack, so we have one *
+ * less stack in that case */
+ while ((max_sequence_len < cards_num -c) && (c > 0))
+ {
+ c--;
+ }
+
+ if (
+ (c > 0) &&
+ ((instance->empty_stacks_fill == FCS_ES_FILLED_BY_KINGS_ONLY) ?
+ (fcs_card_card_num(fcs_stack_card(state, stack, c)) == 13) :
+ 1
+ )
+ )
+ {
+ sfs_check_state_begin();
+
+
+ for(ds=0;ds<state_stacks_num;ds++)
+ {
+ if (fcs_stack_len(state, ds) == 0)
+ break;
+ }
+
+ my_copy_stack(ds);
+
+ for(a=c ; a <= cards_num-1 ; a++)
+ {
+ fcs_push_stack_card_into_stack(new_state, ds, stack, a);
+ }
+
+ my_copy_stack(stack);
+
+ for(a=0;a<cards_num-c;a++)
+ {
+ fcs_pop_stack_card(new_state, stack, temp_card);
+ }
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
+ fcs_move_set_src_stack(temp_move,stack);
+ fcs_move_set_dest_stack(temp_move,ds);
+ fcs_move_set_num_cards_in_seq(temp_move,cards_num-c);
+
+ fcs_move_stack_push(moves, temp_move);
+
+
+ sfs_check_state_end()
+ }
+ }
+ else
+ {
+ num_cards_to_relocate = cards_num - seq_end - 1;
+
+ freecells_to_fill = min(num_cards_to_relocate, num_freecells);
+
+ num_cards_to_relocate -= freecells_to_fill;
+
+ if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD)
+ {
+ freestacks_to_fill = min(num_cards_to_relocate, num_freestacks);
+
+ num_cards_to_relocate -= freestacks_to_fill;
+ }
+ else
+ {
+ freestacks_to_fill = 0;
+ }
+
+ if ((num_cards_to_relocate == 0) && (num_freestacks-freestacks_to_fill > 0))
+ {
+ /* We can move it */
+ int seq_start = c;
+ while (
+ (calc_max_sequence_move(
+ num_freecells-freecells_to_fill,
+ num_freestacks-freestacks_to_fill-1) < seq_end-seq_start+1)
+ &&
+ (seq_start <= seq_end)
+ )
+ {
+ seq_start++;
+ }
+ if ((seq_start <= seq_end) &&
+ ((instance->empty_stacks_fill == FCS_ES_FILLED_BY_KINGS_ONLY) ?
+ (fcs_card_card_num(fcs_stack_card(state, stack, seq_start)) == 13) :
+ 1
+ )
+ )
+ {
+ sfs_check_state_begin();
+
+
+ /* Fill the freecells with the top cards */
+
+ my_copy_stack(stack);
+
+ for(a=0; a<freecells_to_fill ; a++)
+ {
+ /* Find a vacant freecell */
+ for(b=0;b<state_freecells_num;b++)
+ {
+ if (fcs_freecell_card_num(new_state, b) == 0)
+ {
+ break;
+ }
+ }
+ fcs_pop_stack_card(new_state, stack, temp_card);
+ fcs_put_card_in_freecell(new_state, b, temp_card);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FREECELL);
+ fcs_move_set_src_stack(temp_move,stack);
+ fcs_move_set_dest_freecell(temp_move,b);
+ fcs_move_stack_push(moves, temp_move);
+ }
+
+ my_copy_stack(stack);
+
+ /* Fill the free stacks with the cards below them */
+ for(a=0; a < freestacks_to_fill ; a++)
+ {
+ /* Find a vacant stack */
+ for(b=0; b<state_stacks_num; b++)
+ {
+ if (fcs_stack_len(new_state, b) == 0)
+ {
+ break;
+ }
+ }
+
+ my_copy_stack(b);
+ fcs_pop_stack_card(new_state, stack, temp_card);
+ fcs_push_card_into_stack(new_state, b, temp_card);
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
+ fcs_move_set_src_stack(temp_move,stack);
+ fcs_move_set_dest_stack(temp_move,b);
+ fcs_move_set_num_cards_in_seq(temp_move,1);
+ fcs_move_stack_push(moves, temp_move);
+ }
+
+ /* Find a vacant stack */
+ for(b=0; b<state_stacks_num; b++)
+ {
+ if (fcs_stack_len(new_state, b) == 0)
+ {
+ break;
+ }
+ }
+
+ my_copy_stack(b);
+
+ for(a=seq_start ; a <= seq_end ; a++)
+ {
+ fcs_push_stack_card_into_stack(new_state, b, stack, a);
+ }
+ for(a=seq_start ; a <= seq_end ; a++)
+ {
+ fcs_pop_stack_card(new_state, stack, temp_card);
+ }
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
+ fcs_move_set_src_stack(temp_move,stack);
+ fcs_move_set_dest_stack(temp_move,b);
+ fcs_move_set_num_cards_in_seq(temp_move,seq_end-seq_start+1);
+ fcs_move_stack_push(moves, temp_move);
+
+ sfs_check_state_end();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+
+
+int freecell_solver_sfs_move_freecell_cards_to_empty_stack(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+ int check;
+ int fc, stack;
+ fcs_card_t card;
+
+ fcs_move_t temp_move;
+
+ int state_freecells_num;
+ int state_stacks_num;
+
+ /* Let's try to put cards that occupy freecells on an empty stack */
+
+ tests_define_accessors();
+
+ if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_NONE)
+ {
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+ }
+
+ state_freecells_num = instance->freecells_num;
+ state_stacks_num = instance->stacks_num;
+
+ for(fc=0;fc<state_freecells_num;fc++)
+ {
+ card = fcs_freecell_card(state, fc);
+ if (
+ (instance->empty_stacks_fill == FCS_ES_FILLED_BY_KINGS_ONLY) ?
+ (fcs_card_card_num(card) == 13) :
+ (fcs_card_card_num(card) != 0)
+ )
+ {
+ for(stack=0;stack<state_stacks_num;stack++)
+ {
+ if (fcs_stack_len(state, stack) == 0)
+ {
+ break;
+ }
+ }
+ if (stack != state_stacks_num)
+ {
+ /* We can move it */
+
+ sfs_check_state_begin();
+
+ my_copy_stack(stack);
+
+ fcs_push_card_into_stack(new_state, stack, card);
+ fcs_empty_freecell(new_state, fc);
+
+ fcs_move_init(temp_move);
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_FREECELL_TO_STACK);
+ fcs_move_set_src_freecell(temp_move,fc);
+ fcs_move_set_dest_stack(temp_move,stack);
+ fcs_move_stack_push(moves, temp_move);
+
+ sfs_check_state_end()
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+int freecell_solver_sfs_move_cards_to_a_different_parent(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+
+ int check;
+
+ int stack, cards_num, c, a, b, ds, dc;
+ int is_seq_in_src, is_seq_in_dest;
+ int num_cards_to_relocate;
+ int dest_cards_num;
+ fcs_card_t card, this_card, prev_card, temp_card;
+ fcs_card_t dest_card, dest_below_card;
+ int freecells_to_fill, freestacks_to_fill;
+
+ fcs_move_t temp_move;
+
+ int state_freecells_num;
+ int state_stacks_num;
+ int sequences_are_built_by;
+
+ tests_define_accessors();
+
+ state_freecells_num = instance->freecells_num;
+ state_stacks_num = instance->stacks_num;
+ sequences_are_built_by = instance->sequences_are_built_by;
+
+ fcs_move_init(temp_move);
+
+ /* This time try to move cards that are already on top of a parent to a different parent */
+
+ for (stack=0;stack<state_stacks_num;stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+
+ for (c=0 ; c<cards_num ; c++)
+ {
+ /* Check if there is a sequence here. */
+ is_seq_in_src = 1;
+ for(a=c+1 ; a<cards_num ; a++)
+ {
+ this_card = fcs_stack_card(state, stack, a);
+ prev_card = fcs_stack_card(state, stack, a-1);
+
+ if (fcs_is_parent_card(this_card,prev_card))
+ {
+ }
+ else
+ {
+ is_seq_in_src = 0;
+ break;
+ }
+ }
+
+ /* Find a card which this card can be put on; */
+
+ card = fcs_stack_card(state, stack, c);
+
+
+ /* Do not move cards that are already found above a suitable parent */
+ a = 1;
+ if (c != 0)
+ {
+ prev_card = fcs_stack_card(state, stack, c-1);
+ if (fcs_is_parent_card(card,prev_card))
+ {
+ a = 0;
+ }
+ }
+ /* And do not move cards that are flipped */
+ if (!a)
+ {
+ this_card = fcs_stack_card(state,stack,c);
+ if (fcs_card_get_flipped(this_card))
+ {
+ a = 0;
+ }
+ }
+ if (!a)
+ {
+ for(ds=0 ; ds<state_stacks_num; ds++)
+ {
+ if (ds != stack)
+ {
+ dest_cards_num = fcs_stack_len(state, ds);
+ for(dc=0;dc<dest_cards_num;dc++)
+ {
+ dest_card = fcs_stack_card(state, ds, dc);
+
+ if (fcs_is_parent_card(card,dest_card))
+ {
+ /* Corresponding cards - see if it is feasible to move
+ the source to the destination. */
+
+ is_seq_in_dest = 0;
+ if (dest_cards_num - 1 > dc)
+ {
+ dest_below_card = fcs_stack_card(state, ds, dc+1);
+ if (fcs_is_parent_card(dest_below_card,dest_card))
+ {
+ is_seq_in_dest = 1;
+ }
+ }
+
+ if (! is_seq_in_dest)
+ {
+ if (is_seq_in_src)
+ {
+ num_cards_to_relocate = dest_cards_num - dc - 1;
+
+ freecells_to_fill = min(num_cards_to_relocate, num_freecells);
+
+ num_cards_to_relocate -= freecells_to_fill;
+
+ if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD)
+ {
+ freestacks_to_fill = min(num_cards_to_relocate, num_freestacks);
+
+ num_cards_to_relocate -= freestacks_to_fill;
+ }
+ else
+ {
+ freestacks_to_fill = 0;
+ }
+
+ if ((num_cards_to_relocate == 0) &&
+ (calc_max_sequence_move(num_freecells-freecells_to_fill, num_freestacks-freestacks_to_fill) >=
+ cards_num - c))
+ {
+ /* We can move it */
+
+ sfs_check_state_begin()
+
+
+ /* Fill the freecells with the top cards */
+
+ my_copy_stack(ds);
+ for(a=0 ; a<freecells_to_fill ; a++)
+ {
+ /* Find a vacant freecell */
+ for(b=0;b<state_freecells_num;b++)
+ {
+ if (fcs_freecell_card_num(new_state, b) == 0)
+ {
+ break;
+ }
+ }
+
+ fcs_pop_stack_card(new_state, ds, temp_card);
+
+ fcs_put_card_in_freecell(new_state, b, temp_card);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FREECELL);
+ fcs_move_set_src_stack(temp_move,ds);
+ fcs_move_set_dest_freecell(temp_move,b);
+ fcs_move_stack_push(moves, temp_move);
+ }
+
+ /* Fill the free stacks with the cards below them */
+ for(a=0; a < freestacks_to_fill ; a++)
+ {
+ /* Find a vacant stack */
+ for(b=0;b<state_stacks_num;b++)
+ {
+ if (fcs_stack_len(new_state, b) == 0)
+ {
+ break;
+ }
+ }
+
+ my_copy_stack(b);
+
+ fcs_pop_stack_card(new_state, ds, temp_card);
+ fcs_push_card_into_stack(new_state, b, temp_card);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
+ fcs_move_set_src_stack(temp_move,ds);
+ fcs_move_set_dest_stack(temp_move,b);
+ fcs_move_set_num_cards_in_seq(temp_move,1);
+ fcs_move_stack_push(moves, temp_move);
+ }
+
+ my_copy_stack(stack);
+
+ for(a=c ; a <= cards_num-1 ; a++)
+ {
+ fcs_push_stack_card_into_stack(new_state, ds, stack, a);
+ }
+
+ for(a=0;a<cards_num-c;a++)
+ {
+ fcs_pop_stack_card(new_state, stack, temp_card);
+ }
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_STACK);
+ fcs_move_set_src_stack(temp_move,stack);
+ fcs_move_set_dest_stack(temp_move,ds);
+ fcs_move_set_num_cards_in_seq(temp_move,cards_num-c);
+ fcs_move_stack_push(moves, temp_move);
+
+ sfs_check_state_end()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+
+
+
+int freecell_solver_sfs_empty_stack_into_freecells(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+
+ int check;
+
+ int stack, cards_num, c, b;
+ fcs_card_t temp_card;
+ int state_stacks_num;
+ int state_freecells_num;
+
+ fcs_move_t temp_move;
+
+ tests_define_accessors();
+
+ if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_NONE)
+ {
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+ }
+
+ state_stacks_num = instance->stacks_num;
+ state_freecells_num = instance->freecells_num;
+
+
+ /* Now, let's try to empty an entire stack into the freecells, so other cards can
+ * inhabit it */
+
+ if (num_freestacks == 0)
+ {
+ for(stack=0;stack<state_stacks_num;stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+ if (cards_num <= num_freecells)
+ {
+ /* We can empty it */
+
+ sfs_check_state_begin()
+
+ my_copy_stack(stack);
+
+ for(c=0;c<cards_num;c++)
+ {
+ /* Find a vacant freecell */
+ for(b=0; b<state_freecells_num; b++)
+ {
+ if (fcs_freecell_card_num(new_state, b) == 0)
+ {
+ break;
+ }
+ }
+ fcs_pop_stack_card(new_state, stack, temp_card);
+
+ fcs_put_card_in_freecell(new_state, b, temp_card);
+
+ fcs_move_init(temp_move);
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_STACK_TO_FREECELL);
+ fcs_move_set_src_stack(temp_move,stack);
+ fcs_move_set_dest_freecell(temp_move,b);
+ fcs_move_stack_push(moves, temp_move);
+ }
+
+ sfs_check_state_end()
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+
+}
+
+int freecell_solver_sfs_yukon_do_nothing(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+int freecell_solver_sfs_yukon_move_card_to_parent(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+
+ int check;
+
+ int stack, cards_num, c, a, ds;
+ int dest_cards_num;
+ fcs_card_t card, temp_card;
+ fcs_card_t dest_card;
+
+ int state_stacks_num;
+ int sequences_are_built_by;
+
+ fcs_move_t temp_move;
+
+ tests_define_accessors();
+
+ state_stacks_num = instance->stacks_num;
+ sequences_are_built_by = instance->sequences_are_built_by;
+
+ for( ds=0 ; ds < state_stacks_num ; ds++ )
+ {
+ dest_cards_num = fcs_stack_len(state, ds);
+ if (dest_cards_num > 0)
+ {
+ dest_card = fcs_stack_card(state, ds, dest_cards_num-1);
+ for( stack=0 ; stack < state_stacks_num ; stack++)
+ {
+ if (stack == ds)
+ {
+ continue;
+ }
+ cards_num = fcs_stack_len(state, stack);
+ for( c=cards_num-1 ; c >= 0 ; c--)
+ {
+ card = fcs_stack_card(state, stack, c);
+ if (fcs_card_get_flipped(card))
+ {
+ break;
+ }
+ if (fcs_is_parent_card(card, dest_card))
+ {
+ /* We can move it there - now let's check to see
+ * if it is already above a suitable parent. */
+ if ((c == 0) ||
+ (! fcs_is_parent_card(card, fcs_stack_card(state, stack, c-1))))
+ {
+ /* Let's move it */
+ sfs_check_state_begin();
+
+ my_copy_stack(stack);
+ my_copy_stack(ds);
+
+ fcs_move_sequence(ds, stack, c, cards_num-1, a);
+
+ fcs_flip_top_card(stack);
+
+ sfs_check_state_end();
+ }
+
+ }
+ }
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+int freecell_solver_sfs_yukon_move_kings_to_empty_stack(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+
+ int check;
+
+ int stack, cards_num, c, a, ds;
+ fcs_card_t card, temp_card;
+
+ int state_stacks_num;
+
+ fcs_move_t temp_move;
+
+ tests_define_accessors();
+
+ if (num_freestacks == 0)
+ {
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+ }
+
+ state_stacks_num = instance->stacks_num;
+
+ for( stack=0 ; stack < state_stacks_num ; stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+ for( c=cards_num-1 ; c >= 1 ; c--)
+ {
+ card = fcs_stack_card(state, stack, c);
+ if (fcs_card_get_flipped(card))
+ {
+ break;
+ }
+ if (fcs_card_card_num(card) == 13)
+ {
+ /* It's a King - so let's move it */
+ sfs_check_state_begin();
+
+
+ for( ds=0 ; ds < state_stacks_num ; ds++)
+ {
+ if (fcs_stack_len(state, ds) == 0)
+ {
+ break;
+ }
+ }
+ my_copy_stack(stack);
+ my_copy_stack(ds);
+ fcs_move_sequence(ds, stack, c, cards_num-1, a);
+
+
+ fcs_flip_top_card(stack);
+
+ sfs_check_state_end();
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+
+
+#ifdef FCS_WITH_TALONS
+/*
+ Let's try to deal the Gypsy-type Talon.
+
+ */
+int freecell_solver_sfs_deal_gypsy_talon(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+
+ int check;
+
+ fcs_card_t temp_card;
+ int a;
+
+ fcs_move_t temp_move;
+
+ tests_define_accessors();
+
+ if (instance->talon_type != FCS_TALON_GYPSY)
+ {
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+ }
+
+ moves = hard_thread->reusable_move_stack;
+ indirect_stacks_buffer = hard_thread->indirect_stacks_buffer;
+
+ if (fcs_talon_pos(state) < fcs_talon_len(state))
+ {
+ sfs_check_state_begin()
+ for(a=0;a<state_stacks_num;a++)
+ {
+ temp_card = fcs_get_talon_card(new_state, fcs_talon_pos(new_state)+a);
+ fcs_push_card_into_stack(new_state,a,temp_card);
+ }
+ fcs_talon_pos(new_state) += state_stacks_num;
+ fcs_move_set_type(temp_move, FCS_MOVE_TYPE_DEAL_GYPSY_TALON);
+ fcs_move_stack_push(moves, temp_move);
+
+ sfs_check_state_end()
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+
+int freecell_solver_sfs_get_card_from_klondike_talon(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+
+
+ fcs_state_with_locations_t * talon_temp;
+
+ fcs_move_t temp_move;
+
+ int check;
+ int num_redeals_left, num_redeals_done, num_cards_moved[2];
+ int first_iter;
+ fcs_card_t card_to_check, top_card;
+ int s;
+ int cards_num;
+ int a;
+
+ tests_define_accessors();
+
+ if (instance->talon_type != FCS_TALON_KLONDIKE)
+ {
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+ }
+
+ /* Duplicate the talon and its parameters into talon_temp */
+ talon_temp = malloc(sizeof(fcs_state_with_locations_t));
+ talon_temp->s.talon = malloc(fcs_klondike_talon_len(state)+1);
+ memcpy(
+ talon_temp->s.talon,
+ ptr_state_with_locations->s.talon,
+ fcs_klondike_talon_len(state)+1
+ );
+ memcpy(
+ talon_temp->s.talon_params,
+ ptr_state_with_locations->s.talon_params,
+ sizeof(ptr_state_with_locations->s.talon_params)
+ );
+
+ /* Make sure we redeal the talon only once */
+ num_redeals_left = fcs_klondike_talon_num_redeals_left(state);
+ if ((num_redeals_left > 0) || (num_redeals_left < 0))
+ {
+ num_redeals_left = 1;
+ }
+ num_redeals_done = 0;
+ num_cards_moved[0] = 0;
+ num_cards_moved[1] = 0;
+
+ first_iter = 1;
+ while (num_redeals_left >= 0)
+ {
+ if ((fcs_klondike_talon_stack_pos(talon_temp->s) == -1) &&
+ (fcs_klondike_talon_queue_pos(talon_temp->s) == fcs_klondike_talon_len(talon_temp->s)))
+ {
+ break;
+ }
+ if ((!first_iter) || (fcs_klondike_talon_stack_pos(talon_temp->s) == -1))
+ {
+ if (fcs_klondike_talon_queue_pos(talon_temp->s) == fcs_klondike_talon_len(talon_temp->s))
+ {
+ if (num_redeals_left > 0)
+ {
+ fcs_klondike_talon_len(talon_temp->s) = fcs_klondike_talon_stack_pos(talon_temp->s);
+ fcs_klondike_talon_redeal_bare(talon_temp->s);
+
+ num_redeals_left--;
+ num_redeals_done++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ fcs_klondike_talon_queue_to_stack(talon_temp->s);
+ num_cards_moved[num_redeals_done]++;
+ }
+ first_iter = 0;
+
+ card_to_check = fcs_klondike_talon_get_top_card(talon_temp->s);
+ for(s=0 ; s<state_stacks_num ; s++)
+ {
+ cards_num = fcs_stack_len(state,s);
+ top_card = fcs_stack_card(state,s,cards_num-1);
+ if (fcs_is_parent_card(card_to_check, top_card))
+ {
+ /* We have a card in the talon that we can move
+ to the stack, so let's move it */
+ sfs_check_state_begin()
+
+ new_state.talon = malloc(fcs_klondike_talon_len(talon_temp->s)+1);
+ memcpy(
+ new_state.talon,
+ talon_temp->s.talon,
+ fcs_klondike_talon_len(talon_temp->s)+1
+ );
+
+ memcpy(
+ ptr_new_state_with_locations->s.talon_params,
+ talon_temp->s.talon_params,
+ sizeof(ptr_state_with_locations->s.talon_params)
+ );
+
+ for(a=0;a<=num_redeals_done;a++)
+ {
+ fcs_move_set_type(temp_move, FCS_MOVE_TYPE_KLONDIKE_FLIP_TALON);
+ fcs_move_set_num_cards_flipped(temp_move, num_cards_moved[a]);
+ fcs_move_stack_push(moves, temp_move);
+ if (a != num_redeals_done)
+ {
+ fcs_move_set_type(temp_move, FCS_MOVE_TYPE_KLONDIKE_REDEAL_TALON);
+ fcs_move_stack_push(moves,temp_move);
+ }
+ }
+ fcs_push_card_into_stack(new_state, s, fcs_klondike_talon_get_top_card(new_state));
+ fcs_move_set_type(temp_move, FCS_MOVE_TYPE_KLONDIKE_TALON_TO_STACK);
+ fcs_move_set_dest_stack(temp_move, s);
+ fcs_klondike_talon_decrement_stack(new_state);
+
+ sfs_check_state_end()
+ }
+ }
+ }
+
+
+
+#if 0
+ cleanup:
+#endif
+ free(talon_temp->s.talon);
+ free(talon_temp);
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+
+}
+
+#endif
+
+int freecell_solver_sfs_atomic_move_card_to_empty_stack(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+ int empty_stacks_filled_by, state_stacks_num;
+ int stack, cards_num;
+ fcs_card_t card, temp_card;
+ fcs_move_t temp_move;
+ int check;
+ int empty_stack_idx;
+
+ tests_define_accessors();
+
+ if (num_freestacks == 0)
+ {
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+ }
+
+ state_stacks_num = instance->stacks_num;
+
+ for(empty_stack_idx=0;empty_stack_idx<state_stacks_num;empty_stack_idx++)
+ {
+ if (fcs_stack_len(state, empty_stack_idx) == 0)
+ {
+ break;
+ }
+ }
+
+ empty_stacks_filled_by = instance->empty_stacks_fill;
+
+ if (empty_stacks_filled_by == FCS_ES_FILLED_BY_NONE)
+ {
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+ }
+
+
+
+ for(stack=0;stack<state_stacks_num;stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+ if (cards_num > 0)
+ {
+ card = fcs_stack_card(state, stack, cards_num-1);
+ if ((empty_stacks_filled_by == FCS_ES_FILLED_BY_KINGS_ONLY) &&
+ (fcs_card_card_num(card) != 13))
+ {
+ continue;
+ }
+ /* Let's move it */
+ {
+ sfs_check_state_begin();
+
+ my_copy_stack(stack);
+
+ fcs_pop_stack_card(new_state, stack, temp_card);
+
+
+ my_copy_stack(empty_stack_idx);
+
+ fcs_push_card_into_stack(new_state, empty_stack_idx, card);
+
+ fcs_move_set_type(temp_move, FCS_MOVE_TYPE_STACK_TO_STACK);
+ fcs_move_set_src_stack(temp_move, stack);
+ fcs_move_set_dest_stack(temp_move, empty_stack_idx);
+ fcs_move_set_num_cards_in_seq(temp_move, 1);
+
+ fcs_move_stack_push(moves, temp_move);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE);
+ fcs_move_stack_push(moves, temp_move);
+
+ sfs_check_state_end()
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+int freecell_solver_sfs_atomic_move_card_to_parent(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+ int state_stacks_num;
+ int stack, cards_num, ds, ds_cards_num;
+ fcs_card_t card, dest_card, temp_card;
+ fcs_move_t temp_move;
+ int check;
+ int sequences_are_built_by;
+
+ tests_define_accessors();
+
+ state_stacks_num = instance->stacks_num;
+
+ sequences_are_built_by = instance->sequences_are_built_by;
+
+
+ for(stack=0;stack<state_stacks_num;stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+ if (cards_num > 0)
+ {
+ card = fcs_stack_card(state, stack, cards_num-1);
+
+ for(ds=0;ds<state_stacks_num;ds++)
+ {
+ if (ds == stack)
+ {
+ continue;
+ }
+
+ ds_cards_num = fcs_stack_len(state, ds);
+ if (ds_cards_num > 0)
+ {
+ dest_card = fcs_stack_card(state, ds, ds_cards_num-1);
+ if (fcs_is_parent_card(card, dest_card))
+ {
+ /* Let's move it */
+ {
+ sfs_check_state_begin();
+
+ my_copy_stack(stack);
+ my_copy_stack(ds);
+
+ fcs_pop_stack_card(new_state, stack, temp_card);
+
+ fcs_push_card_into_stack(new_state, ds, card);
+
+ fcs_move_set_type(temp_move, FCS_MOVE_TYPE_STACK_TO_STACK);
+ fcs_move_set_src_stack(temp_move, stack);
+ fcs_move_set_dest_stack(temp_move, ds);
+ fcs_move_set_num_cards_in_seq(temp_move, 1);
+
+ fcs_move_stack_push(moves, temp_move);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE);
+ fcs_move_stack_push(moves, temp_move);
+
+ sfs_check_state_end()
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+int freecell_solver_sfs_atomic_move_card_to_freecell(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+ int state_stacks_num;
+ int state_freecells_num;
+ int stack, cards_num, ds;
+ fcs_card_t card, temp_card;
+ fcs_move_t temp_move;
+ int check;
+ int sequences_are_built_by;
+
+ tests_define_accessors();
+
+ state_stacks_num = instance->stacks_num;
+ state_freecells_num = instance->freecells_num;
+
+ sequences_are_built_by = instance->sequences_are_built_by;
+
+ if (num_freecells == 0)
+ {
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+ }
+
+ for(ds=0;ds<state_freecells_num;ds++)
+ {
+ if (fcs_freecell_card_num(state, ds) == 0)
+ {
+ break;
+ }
+ }
+
+
+
+ for(stack=0;stack<state_stacks_num;stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+ if (cards_num > 0)
+ {
+ card = fcs_stack_card(state, stack, cards_num-1);
+
+ /* Let's move it */
+ {
+ sfs_check_state_begin();
+
+ my_copy_stack(stack);
+
+ fcs_pop_stack_card(new_state, stack, temp_card);
+
+ fcs_put_card_in_freecell(new_state, ds, card);
+
+ fcs_move_init(temp_move);
+ fcs_move_set_type(temp_move, FCS_MOVE_TYPE_STACK_TO_FREECELL);
+ fcs_move_set_src_stack(temp_move, stack);
+ fcs_move_set_dest_freecell(temp_move, ds);
+
+ fcs_move_stack_push(moves, temp_move);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE);
+ fcs_move_stack_push(moves, temp_move);
+
+ sfs_check_state_end()
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+int freecell_solver_sfs_atomic_move_freecell_card_to_parent(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+ int state_stacks_num, state_freecells_num;
+ int fc, ds, ds_cards_num;
+ fcs_card_t card, dest_card;
+ fcs_move_t temp_move;
+ int check;
+ int sequences_are_built_by;
+
+ tests_define_accessors();
+
+ state_stacks_num = instance->stacks_num;
+ state_freecells_num = instance->freecells_num;
+
+ sequences_are_built_by = instance->sequences_are_built_by;
+
+
+
+
+ for(fc=0;fc<state_freecells_num;fc++)
+ {
+ card = fcs_freecell_card(state, fc);
+ if (fcs_card_card_num(card) == 0)
+ {
+ continue;
+ }
+
+ for(ds=0;ds<state_stacks_num;ds++)
+ {
+ ds_cards_num = fcs_stack_len(state, ds);
+ if (ds_cards_num > 0)
+ {
+ dest_card = fcs_stack_card(state, ds, ds_cards_num-1);
+ if (fcs_is_parent_card(card, dest_card))
+ {
+ /* Let's move it */
+ {
+ sfs_check_state_begin();
+
+ my_copy_stack(ds);
+
+ fcs_empty_freecell(new_state, fc);
+
+ fcs_push_card_into_stack(new_state, ds, card);
+
+ fcs_move_set_type(temp_move, FCS_MOVE_TYPE_FREECELL_TO_STACK);
+ fcs_move_set_src_freecell(temp_move, fc);
+ fcs_move_set_dest_stack(temp_move, ds);
+ fcs_move_set_num_cards_in_seq(temp_move, 1);
+
+ fcs_move_stack_push(moves, temp_move);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE);
+ fcs_move_stack_push(moves, temp_move);
+
+ sfs_check_state_end()
+ }
+ }
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+int freecell_solver_sfs_atomic_move_freecell_card_to_empty_stack(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+ int state_stacks_num, state_freecells_num;
+ int fc, ds;
+ fcs_card_t card;
+ fcs_move_t temp_move;
+ int check;
+ int sequences_are_built_by, empty_stacks_filled_by;
+
+ tests_define_accessors();
+
+ moves = hard_thread->reusable_move_stack;
+ indirect_stacks_buffer = hard_thread->indirect_stacks_buffer;
+
+ state_stacks_num = instance->stacks_num;
+ state_freecells_num = instance->freecells_num;
+
+ sequences_are_built_by = instance->sequences_are_built_by;
+
+ if (num_freestacks == 0)
+ {
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+ }
+
+ empty_stacks_filled_by = instance->empty_stacks_fill;
+
+ if (empty_stacks_filled_by == FCS_ES_FILLED_BY_NONE)
+ {
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+ }
+
+ for(ds=0;ds<state_stacks_num;ds++)
+ {
+ if (fcs_stack_len(state, ds) == 0)
+ {
+ break;
+ }
+ }
+
+ for(fc=0;fc<state_freecells_num;fc++)
+ {
+ card = fcs_freecell_card(state, fc);
+ if (fcs_card_card_num(card) == 0)
+ {
+ continue;
+ }
+
+ if ((empty_stacks_filled_by == FCS_ES_FILLED_BY_KINGS_ONLY) &&
+ (fcs_card_card_num(card) != 13))
+ {
+ continue;
+ }
+
+ {
+ sfs_check_state_begin();
+
+ my_copy_stack(ds);
+
+ fcs_empty_freecell(new_state, fc);
+
+ fcs_push_card_into_stack(new_state, ds, card);
+
+ fcs_move_set_type(temp_move, FCS_MOVE_TYPE_FREECELL_TO_STACK);
+ fcs_move_set_src_freecell(temp_move, fc);
+ fcs_move_set_dest_stack(temp_move, ds);
+ fcs_move_set_num_cards_in_seq(temp_move, 1);
+
+ fcs_move_stack_push(moves, temp_move);
+
+ fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE);
+ fcs_move_stack_push(moves, temp_move);
+
+ sfs_check_state_end()
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+
+#undef state_with_locations
+#undef state
+#undef new_state_with_locations
+#undef new_state
+
+
+freecell_solver_solve_for_state_test_t freecell_solver_sfs_tests[FCS_TESTS_NUM] =
+{
+ freecell_solver_sfs_move_top_stack_cards_to_founds,
+ freecell_solver_sfs_move_freecell_cards_to_founds,
+ freecell_solver_sfs_move_freecell_cards_on_top_of_stacks,
+ freecell_solver_sfs_move_non_top_stack_cards_to_founds,
+ freecell_solver_sfs_move_stack_cards_to_different_stacks,
+ freecell_solver_sfs_move_stack_cards_to_a_parent_on_the_same_stack,
+ freecell_solver_sfs_move_sequences_to_free_stacks,
+ freecell_solver_sfs_move_freecell_cards_to_empty_stack,
+ freecell_solver_sfs_move_cards_to_a_different_parent,
+ freecell_solver_sfs_empty_stack_into_freecells,
+ freecell_solver_sfs_simple_simon_move_sequence_to_founds,
+ freecell_solver_sfs_simple_simon_move_sequence_to_true_parent,
+ freecell_solver_sfs_simple_simon_move_whole_stack_sequence_to_false_parent,
+ freecell_solver_sfs_simple_simon_move_sequence_to_true_parent_with_some_cards_above,
+ freecell_solver_sfs_simple_simon_move_sequence_with_some_cards_above_to_true_parent,
+ freecell_solver_sfs_simple_simon_move_sequence_with_junk_seq_above_to_true_parent_with_some_cards_above,
+ freecell_solver_sfs_simple_simon_move_whole_stack_sequence_to_false_parent_with_some_cards_above,
+ freecell_solver_sfs_simple_simon_move_sequence_to_parent_on_the_same_stack,
+ freecell_solver_sfs_atomic_move_card_to_empty_stack,
+ freecell_solver_sfs_atomic_move_card_to_parent,
+ freecell_solver_sfs_atomic_move_card_to_freecell,
+ freecell_solver_sfs_atomic_move_freecell_card_to_parent,
+ freecell_solver_sfs_atomic_move_freecell_card_to_empty_stack,
+#if 0
+ freecell_solver_sfs_move_top_stack_cards_to_founds,
+ freecell_solver_sfs_yukon_move_card_to_parent,
+ freecell_solver_sfs_yukon_move_kings_to_empty_stack,
+ freecell_solver_sfs_yukon_do_nothing,
+ freecell_solver_sfs_yukon_do_nothing,
+#endif
+ freecell_solver_sfs_yukon_do_nothing,
+ freecell_solver_sfs_yukon_do_nothing
+#ifdef FCS_WITH_TALONS
+ ,
+ freecell_solver_sfs_deal_gypsy_talon,
+ freecell_solver_sfs_get_card_from_klondike_talon
+#endif
+};
diff --git a/kpat/freecell-solver/inline.h b/kpat/freecell-solver/inline.h
new file mode 100644
index 00000000..81f4f8e1
--- /dev/null
+++ b/kpat/freecell-solver/inline.h
@@ -0,0 +1,20 @@
+/*
+ * inline.h - the purpose of this file is to define the GCC_INLINE
+ * macro.
+ *
+ * Written by Shlomi Fish, 2002
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ * */
+
+#ifndef FC_SOLVE__INLINE_H
+#define FC_SOLVE__INLINE_H
+
+#if defined(__GNUC__)
+#define GCC_INLINE __inline__
+#else
+#define GCC_INLINE
+#endif
+
+
+#endif
diff --git a/kpat/freecell-solver/intrface.c b/kpat/freecell-solver/intrface.c
new file mode 100644
index 00000000..6551652b
--- /dev/null
+++ b/kpat/freecell-solver/intrface.c
@@ -0,0 +1,1764 @@
+/*
+ * intrface.c - instance interface functions for Freecell Solver
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000-2001
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <stdio.h>
+#include <math.h>
+#include <ctype.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define NUM_TIMES_STEP 50
+
+#include "fcs_config.h"
+
+/* So the FCS_STATE_STORAGE macros would be defined */
+#if FCS_STATE_STORAGE==FCS_STATE_STORAGE_LIBREDBLACK_TREE
+#include <search.h>
+#endif
+
+#include "state.h"
+#include "card.h"
+#include "fcs_dm.h"
+#include "fcs.h"
+
+#include "fcs_isa.h"
+
+#include "caas.h"
+
+#include "preset.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+/*
+ General use of this interface:
+ 1. freecell_solver_alloc_instance()
+ 2. Set the parameters of the game
+ 3. If you wish to revert, go to step #11.
+ 4. freecell_solver_init_instance()
+ 5. Call freecell_solver_solve_instance() with the initial board.
+ 6. If it returns FCS_STATE_SUSPEND_PROCESS and you wish to proceed,
+ then increase the iteration limit and call
+ freecell_solver_resume_instance().
+ 7. Repeat Step #6 zero or more times.
+ 8. If the last call to solve_instance() or resume_instance() returned
+ FCS_STATE_SUSPEND_PROCESS then call
+ freecell_solver_unresume_instance().
+ 9. If the solving was successful you can use the move stacks or the
+ intermediate stacks. (Just don't destory them in any way).
+ 10. Call freecell_solver_finish_instance().
+ 11. Call freecell_solver_free_instance().
+
+ The library functions inside lib.c (a.k.a fcs_user()) give an
+ easier approach for embedding Freecell Solver into your library. The
+ intent of this comment is to document the code, rather than to be
+ a guideline for the user.
+*/
+
+#if 0
+static const double freecell_solver_a_star_default_weights[5] = {0.5,0,0.5,0,0};
+#else
+static const double freecell_solver_a_star_default_weights[5] = {0.5,0,0.3,0,0.2};
+#endif
+
+
+
+
+
+
+
+static void freecell_solver_initialize_bfs_queue(freecell_solver_soft_thread_t * soft_thread)
+{
+ /* Initialize the BFS queue. We have one dummy element at the beginning
+ in order to make operations simpler. */
+ soft_thread->bfs_queue = (fcs_states_linked_list_item_t*)malloc(sizeof(fcs_states_linked_list_item_t));
+ soft_thread->bfs_queue->next = (fcs_states_linked_list_item_t*)malloc(sizeof(fcs_states_linked_list_item_t));
+ soft_thread->bfs_queue_last_item = soft_thread->bfs_queue->next;
+ soft_thread->bfs_queue_last_item->next = NULL;
+}
+
+static void foreach_soft_thread(
+ freecell_solver_instance_t * instance,
+ void (*soft_thread_callback)(
+ freecell_solver_soft_thread_t * soft_thread,
+ void * context
+ ),
+ void * context
+ )
+
+{
+ int ht_idx, st_idx;
+ freecell_solver_hard_thread_t * hard_thread;
+ int num_soft_threads;
+ freecell_solver_soft_thread_t * * ht_soft_threads;
+ for(ht_idx = 0 ; ht_idx<instance->num_hard_threads; ht_idx++)
+ {
+ hard_thread = instance->hard_threads[ht_idx];
+ num_soft_threads = hard_thread->num_soft_threads;
+ ht_soft_threads = hard_thread->soft_threads;
+ for(st_idx = 0 ; st_idx < num_soft_threads; st_idx++)
+ {
+ soft_thread_callback(ht_soft_threads[st_idx], context);
+ }
+ }
+
+ if (instance->optimization_thread)
+ {
+ soft_thread_callback(instance->optimization_thread->soft_threads[0], context);
+ }
+}
+
+
+
+static void soft_thread_clean_soft_dfs(
+ freecell_solver_soft_thread_t * soft_thread,
+ void * context
+ )
+{
+ int num_solution_states;
+ int dfs_max_depth;
+ fcs_soft_dfs_stack_item_t * soft_dfs_info, * info_ptr;
+ /* Check if a Soft-DFS-type scan was called in the first place */
+ if (soft_thread->soft_dfs_info == NULL)
+ {
+ /* If not - do nothing */
+ return;
+ }
+
+ (void)context;
+ soft_dfs_info = soft_thread->soft_dfs_info;
+ num_solution_states = soft_thread->num_solution_states;
+ dfs_max_depth = soft_thread->dfs_max_depth;
+ /* De-allocate the Soft-DFS specific stacks */
+ {
+ int depth;
+ info_ptr = soft_dfs_info;
+ for(depth=0;depth<num_solution_states-1;depth++)
+ {
+ free(info_ptr->derived_states_list.states);
+ free(info_ptr->derived_states_random_indexes);
+ info_ptr++;
+ }
+ for(;depth<dfs_max_depth;depth++)
+ {
+ if (info_ptr->derived_states_list.max_num_states)
+ {
+ free(info_ptr->derived_states_list.states);
+ free(info_ptr->derived_states_random_indexes);
+ }
+ info_ptr++;
+ }
+
+ free(soft_dfs_info);
+
+ soft_thread->soft_dfs_info = NULL;
+
+ soft_thread->dfs_max_depth = 0;
+
+ }
+}
+
+static void clean_soft_dfs(
+ freecell_solver_instance_t * instance
+ )
+{
+ foreach_soft_thread(instance, soft_thread_clean_soft_dfs, NULL);
+}
+
+static freecell_solver_soft_thread_t * alloc_soft_thread(
+ freecell_solver_hard_thread_t * hard_thread
+ )
+{
+ freecell_solver_soft_thread_t * soft_thread;
+ unsigned int a;
+
+ /* Make sure we are not exceeding the maximal number of soft threads
+ * for an instance. */
+ if (hard_thread->instance->next_soft_thread_id == MAX_NUM_SCANS)
+ {
+ return NULL;
+ }
+
+ soft_thread = malloc(sizeof(freecell_solver_soft_thread_t));
+
+ soft_thread->hard_thread = hard_thread;
+
+ soft_thread->id = (hard_thread->instance->next_soft_thread_id)++;
+
+ soft_thread->dfs_max_depth = 0;
+
+ soft_thread->tests_order.num = 0;
+ soft_thread->tests_order.tests = NULL;
+ soft_thread->tests_order.max_num = 0;
+
+
+ /* Initialize all the Soft-DFS stacks to NULL */
+ soft_thread->soft_dfs_info = NULL;
+
+ /* The default solving method */
+ soft_thread->method = FCS_METHOD_SOFT_DFS;
+
+ soft_thread->orig_method = FCS_METHOD_NONE;
+
+ freecell_solver_initialize_bfs_queue(soft_thread);
+
+ /* Initialize the priotity queue of the A* scan */
+ soft_thread->a_star_pqueue = malloc(sizeof(PQUEUE));
+ freecell_solver_PQueueInitialise(
+ soft_thread->a_star_pqueue,
+ 1024
+ );
+
+ /* Set the default A* weigths */
+ for(a=0;a<(sizeof(soft_thread->a_star_weights)/sizeof(soft_thread->a_star_weights[0]));a++)
+ {
+ soft_thread->a_star_weights[a] = freecell_solver_a_star_default_weights[a];
+ }
+
+ soft_thread->rand_gen = freecell_solver_rand_alloc(soft_thread->rand_seed = 24);
+
+ soft_thread->initialized = 0;
+
+ soft_thread->num_times_step = NUM_TIMES_STEP;
+
+#if 0
+ {
+ char * no_use;
+ freecell_solver_apply_tests_order(soft_thread, "[01][23456789]", &no_use);
+ }
+#else
+ soft_thread->tests_order.num = soft_thread->hard_thread->instance->instance_tests_order.num;
+ soft_thread->tests_order.tests =
+ malloc(sizeof(soft_thread->tests_order.tests[0]) * soft_thread->tests_order.num);
+ memcpy(soft_thread->tests_order.tests,
+ soft_thread->hard_thread->instance->instance_tests_order.tests,
+ sizeof(soft_thread->tests_order.tests[0]) * soft_thread->tests_order.num
+ );
+ soft_thread->tests_order.max_num = soft_thread->tests_order.num;
+#endif
+
+ soft_thread->is_finished = 0;
+
+ soft_thread->name = NULL;
+
+ return soft_thread;
+}
+
+static freecell_solver_hard_thread_t * alloc_hard_thread(
+ freecell_solver_instance_t * instance
+ )
+{
+ freecell_solver_hard_thread_t * hard_thread;
+
+ /* Make sure we are not exceeding the maximal number of soft threads
+ * for an instance. */
+ if (instance->next_soft_thread_id == MAX_NUM_SCANS)
+ {
+ return NULL;
+ }
+
+ hard_thread = malloc(sizeof(freecell_solver_hard_thread_t));
+
+ hard_thread->instance = instance;
+
+ hard_thread->num_times = 0;
+
+ hard_thread->num_soft_threads = 1;
+
+ hard_thread->soft_threads =
+ malloc(sizeof(hard_thread->soft_threads[0]) *
+ hard_thread->num_soft_threads
+ );
+
+ hard_thread->soft_threads[0] = alloc_soft_thread(hard_thread);
+
+ /* Set a limit on the Hard-Thread's scan. */
+ hard_thread->num_times_step = NUM_TIMES_STEP;
+
+ hard_thread->ht_max_num_times = hard_thread->num_times_step;
+
+ hard_thread->max_num_times = -1;
+
+ hard_thread->num_soft_threads_finished = 0;
+
+#ifdef INDIRECT_STACK_STATES
+ hard_thread->stacks_allocator =
+ freecell_solver_compact_allocator_new();
+#endif
+ hard_thread->move_stacks_allocator =
+ freecell_solver_compact_allocator_new();
+
+ fcs_move_stack_alloc_into_var(hard_thread->reusable_move_stack);
+
+ hard_thread->prelude_as_string = NULL;
+ hard_thread->prelude = NULL;
+ hard_thread->prelude_num_items = 0;
+ hard_thread->prelude_idx = 0;
+
+ return hard_thread;
+}
+
+
+/*
+ This function allocates a Freecell Solver instance struct and set the
+ default values in it. After the call to this function, the program can
+ set parameters in it which are different from the default.
+
+ Afterwards freecell_solver_init_instance() should be called in order
+ to really prepare it for solving.
+ */
+freecell_solver_instance_t * freecell_solver_alloc_instance(void)
+{
+ freecell_solver_instance_t * instance;
+
+ instance = malloc(sizeof(freecell_solver_instance_t));
+
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT)
+ instance->num_indirect_prev_states = 0;
+ instance->max_num_indirect_prev_states = 0;
+#endif
+
+ instance->num_times = 0;
+
+ instance->num_states_in_collection = 0;
+
+ instance->max_num_times = -1;
+ instance->max_depth = -1;
+ instance->max_num_states_in_collection = -1;
+
+ instance->instance_tests_order.num = 0;
+ instance->instance_tests_order.tests = NULL;
+ instance->instance_tests_order.max_num = 0;
+
+ instance->opt_tests_order_set = 0;
+
+ instance->opt_tests_order.num = 0;
+ instance->opt_tests_order.tests = NULL;
+ instance->opt_tests_order.max_num = 0;
+
+
+
+#ifdef FCS_WITH_TALONS
+ instance->talon_type = FCS_TALON_NONE;
+#endif
+
+ instance->num_hard_threads = 0;
+
+ freecell_solver_apply_preset_by_name(instance, "freecell");
+
+ /****************************************/
+
+ instance->debug_iter_output = 0;
+
+ instance->next_soft_thread_id = 0;
+
+ instance->num_hard_threads = 1;
+
+ instance->hard_threads = malloc(sizeof(instance->hard_threads[0]) * instance->num_hard_threads);
+
+ instance->hard_threads[0] = alloc_hard_thread(instance);
+
+ instance->solution_moves = NULL;
+
+ instance->optimize_solution_path = 0;
+
+#ifdef FCS_WITH_MHASH
+ instance->mhash_type = MHASH_MD5;
+#endif
+
+ instance->optimization_thread = NULL;
+
+ instance->num_hard_threads_finished = 0;
+
+ instance->calc_real_depth = 0;
+
+ instance->to_reparent_states = 0;
+
+ /* Make the 1 the default, because otherwise scans will not cooperate
+ * with one another. */
+ instance->scans_synergy = 1;
+
+ return instance;
+}
+
+
+
+
+
+static void free_bfs_queue(freecell_solver_soft_thread_t * soft_thread)
+{
+ /* Free the BFS linked list */
+ fcs_states_linked_list_item_t * item, * next_item;
+ item = soft_thread->bfs_queue;
+ while (item != NULL)
+ {
+ next_item = item->next;
+ free(item);
+ item = next_item;
+ }
+}
+
+static void free_instance_soft_thread_callback(freecell_solver_soft_thread_t * soft_thread, void * context)
+{
+ (void)context;
+ free_bfs_queue(soft_thread);
+ freecell_solver_rand_free(soft_thread->rand_gen);
+
+ freecell_solver_PQueueFree(soft_thread->a_star_pqueue);
+ free(soft_thread->a_star_pqueue);
+
+ free(soft_thread->tests_order.tests);
+
+ if (soft_thread->name != NULL)
+ {
+ free(soft_thread->name);
+ }
+ /* The data-structure itself was allocated */
+ free(soft_thread);
+}
+
+static void free_instance_hard_thread_callback(freecell_solver_hard_thread_t * hard_thread)
+{
+ if (hard_thread->prelude_as_string)
+ {
+ free (hard_thread->prelude_as_string);
+ }
+ if (hard_thread->prelude)
+ {
+ free (hard_thread->prelude);
+ }
+ fcs_move_stack_destroy(hard_thread->reusable_move_stack);
+
+ free(hard_thread->soft_threads);
+
+ if (hard_thread->move_stacks_allocator)
+ {
+ freecell_solver_compact_allocator_finish(hard_thread->move_stacks_allocator);
+ }
+#ifdef INDIRECT_STACK_STATES
+ if (hard_thread->stacks_allocator)
+ {
+ freecell_solver_compact_allocator_finish(hard_thread->stacks_allocator);
+ }
+#endif
+ free(hard_thread);
+}
+
+/*
+ This function is the last function that should be called in the
+ sequence of operations on instance, and it is meant for de-allocating
+ whatever memory was allocated by alloc_instance().
+ */
+void freecell_solver_free_instance(freecell_solver_instance_t * instance)
+{
+ int ht_idx;
+
+ foreach_soft_thread(instance, free_instance_soft_thread_callback, NULL);
+
+ for(ht_idx=0; ht_idx < instance->num_hard_threads; ht_idx++)
+ {
+ free_instance_hard_thread_callback(instance->hard_threads[ht_idx]);
+ }
+ free(instance->hard_threads);
+ if (instance->optimization_thread)
+ {
+ free_instance_hard_thread_callback(instance->optimization_thread);
+ }
+
+ free(instance->instance_tests_order.tests);
+
+ if (instance->opt_tests_order_set)
+ {
+ free(instance->opt_tests_order.tests);
+ }
+
+ free(instance);
+}
+
+
+static void normalize_a_star_weights(
+ freecell_solver_soft_thread_t * soft_thread,
+ void * context
+ )
+{
+ /* Normalize the A* Weights, so the sum of all of them would be 1. */
+ double sum;
+ unsigned int a;
+ sum = 0;
+ for(a=0;a<(sizeof(soft_thread->a_star_weights)/sizeof(soft_thread->a_star_weights[0]));a++)
+ {
+ if (soft_thread->a_star_weights[a] < 0)
+ {
+ soft_thread->a_star_weights[a] = freecell_solver_a_star_default_weights[a];
+ }
+ sum += soft_thread->a_star_weights[a];
+ }
+ if (sum == 0)
+ {
+ sum = 1;
+ }
+ for(a=0;a<(sizeof(soft_thread->a_star_weights)/sizeof(soft_thread->a_star_weights[0]));a++)
+ {
+ soft_thread->a_star_weights[a] /= sum;
+ }
+ (void)context;
+}
+
+static void accumulate_tests_order(
+ freecell_solver_soft_thread_t * soft_thread,
+ void * context
+ )
+{
+ int * tests_order = (int *)context;
+ int a;
+ for(a=0;a<soft_thread->tests_order.num;a++)
+ {
+ *tests_order |= (1 << (soft_thread->tests_order.tests[a] & FCS_TEST_ORDER_NO_FLAGS_MASK));
+ }
+}
+
+static void determine_scan_completeness(
+ freecell_solver_soft_thread_t * soft_thread,
+ void * context
+ )
+{
+ int global_tests_order = *(int *)context;
+ int tests_order = 0;
+ int a;
+ for(a=0;a<soft_thread->tests_order.num;a++)
+ {
+ tests_order |= (1 << (soft_thread->tests_order.tests[a] & FCS_TEST_ORDER_NO_FLAGS_MASK));
+ }
+ soft_thread->is_a_complete_scan = (tests_order == global_tests_order);
+}
+
+enum FCS_COMPILE_PRELUDE_ERRORS_T
+{
+ FCS_COMPILE_PRELUDE_OK,
+ FCS_COMPILE_PRELUDE_NO_AT_SIGN,
+ FCS_COMPILE_PRELUDE_UNKNOWN_SCAN_ID
+};
+
+static int compile_prelude(
+ freecell_solver_hard_thread_t * hard_thread
+ )
+{
+ char * p_quota, * p_scan, * p;
+ char * string;
+ int last_one = 0;
+ int num_items = 0;
+ int max_num_items = 16;
+ fcs_prelude_item_t * prelude;
+ int st_idx;
+
+ prelude = malloc(sizeof(prelude[0]) * max_num_items);
+ string = hard_thread->prelude_as_string;
+
+ p = string;
+
+ while (! last_one)
+ {
+ p_quota = p;
+ while((*p) && isdigit(*p))
+ {
+ p++;
+ }
+ if (*p != '@')
+ {
+ free(prelude);
+ return FCS_COMPILE_PRELUDE_NO_AT_SIGN;
+ }
+ *p = '\0';
+ p++;
+ p_scan = p;
+ while((*p) && ((*p) != ','))
+ {
+ p++;
+ }
+ if ((*p) == '\0')
+ {
+ last_one = 1;
+ }
+ *p = '\0';
+ p++;
+
+ for(st_idx = 0; st_idx < hard_thread->num_soft_threads ; st_idx++)
+ {
+ if (!strcmp(hard_thread->soft_threads[st_idx]->name, p_scan))
+ {
+ break;
+ }
+ }
+ if (st_idx == hard_thread->num_soft_threads)
+ {
+ free(prelude);
+ return FCS_COMPILE_PRELUDE_UNKNOWN_SCAN_ID;
+ }
+ prelude[num_items].scan_idx = st_idx;
+ prelude[num_items].quota = atoi(p_quota);
+ num_items++;
+ if (num_items == max_num_items)
+ {
+ max_num_items += 16;
+ prelude = realloc(prelude, sizeof(prelude[0]) * max_num_items);
+ }
+ }
+
+ hard_thread->prelude = prelude;
+ hard_thread->prelude_num_items = num_items;
+ hard_thread->prelude_idx = 0;
+
+ return FCS_COMPILE_PRELUDE_OK;
+}
+
+
+void freecell_solver_init_instance(freecell_solver_instance_t * instance)
+{
+ int ht_idx;
+ freecell_solver_hard_thread_t * hard_thread;
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT)
+ instance->num_prev_states_margin = 0;
+
+ instance->max_num_indirect_prev_states = PREV_STATES_GROW_BY;
+
+ instance->indirect_prev_states = (fcs_state_with_locations_t * *)malloc(sizeof(fcs_state_with_locations_t *) * instance->max_num_indirect_prev_states);
+#endif
+
+ /* Initialize the state packs */
+ for(ht_idx=0;ht_idx<instance->num_hard_threads;ht_idx++)
+ {
+ hard_thread = instance->hard_threads[ht_idx];
+ if (hard_thread->prelude_as_string)
+ {
+ compile_prelude(hard_thread);
+ }
+ hard_thread->num_times_left_for_soft_thread =
+ hard_thread->soft_threads[0]->num_times_step;
+ freecell_solver_state_ia_init(hard_thread);
+ }
+
+ /* Normalize the A* Weights, so the sum of all of them would be 1. */
+ foreach_soft_thread(instance, normalize_a_star_weights, NULL);
+
+ {
+ int total_tests = 0;
+ foreach_soft_thread(instance, accumulate_tests_order, &total_tests);
+ foreach_soft_thread(instance, determine_scan_completeness, &total_tests);
+ if (instance->opt_tests_order_set == 0)
+ {
+ /*
+ *
+ * What this code does is convert the bit map of total_tests
+ * to a valid tests order.
+ *
+ * */
+ int bit_idx, num_tests = 0;
+ int * tests = malloc(sizeof(total_tests)*8*sizeof(tests[0]));
+
+ for(bit_idx=0; total_tests != 0; bit_idx++, total_tests >>= 1)
+ {
+ if ((total_tests & 0x1) != 0)
+ {
+ tests[num_tests++] = bit_idx;
+ }
+ }
+ tests = realloc(tests, num_tests*sizeof(tests[0]));
+ instance->opt_tests_order.tests = tests;
+ instance->opt_tests_order.num =
+ instance->opt_tests_order.max_num =
+ num_tests;
+ instance->opt_tests_order_set = 1;
+ }
+ }
+
+
+}
+
+
+
+
+/* These are all stack comparison functions to be used for the stacks
+ cache when using INDIRECT_STACK_STATES
+*/
+#if defined(INDIRECT_STACK_STATES)
+
+extern int freecell_solver_stack_compare_for_comparison(const void * v_s1, const void * v_s2);
+
+#if ((FCS_STACK_STORAGE != FCS_STACK_STORAGE_GLIB_TREE) && (FCS_STACK_STORAGE != FCS_STACK_STORAGE_GLIB_HASH))
+static int fcs_stack_compare_for_comparison_with_context(
+ const void * v_s1,
+ const void * v_s2,
+#if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE)
+ const
+#endif
+ void * context
+
+ )
+{
+ (void)context;
+ return freecell_solver_stack_compare_for_comparison(v_s1, v_s2);
+}
+#endif
+
+
+
+
+
+#if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH)
+/* A hash calculation function for use in glib's hash */
+static guint freecell_solver_glib_hash_stack_hash_function (
+ gconstpointer key
+ )
+{
+ guint hash_value_int;
+ /* Calculate the hash value for the stack */
+ /* This hash function was ripped from the Perl source code.
+ * (It is not derived work however). */
+ const char * s_ptr = (char*)key;
+ const char * s_end = s_ptr+fcs_standalone_stack_len((fcs_card_t *)key)+1;
+ hash_value_int = 0;
+ while (s_ptr < s_end)
+ {
+ hash_value_int += (hash_value_int << 5) + *(s_ptr++);
+ }
+ hash_value_int += (hash_value_int >> 5);
+
+}
+
+
+
+
+
+static gint freecell_solver_glib_hash_stack_compare (
+ gconstpointer a,
+ gconstpointer b
+)
+{
+ return !(fcs_stack_compare_for_comparison(a,b));
+}
+#endif /* (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH) */
+
+
+
+
+
+#endif /* defined(INDIRECT_STACK_STATES) */
+
+
+
+
+
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH)
+/*
+ * This hash function is defined in caas.c
+ *
+ * */
+extern guint freecell_solver_hash_function(gconstpointer key);
+#endif
+
+/*
+ * This function traces the solution from the final state down
+ * to the initial state
+ * */
+static void trace_solution(
+ freecell_solver_instance_t * instance
+ )
+{
+ /*
+ Trace the solution.
+ */
+ fcs_state_with_locations_t * s1;
+ fcs_move_stack_t * solution_moves;
+ int move_idx;
+ fcs_move_stack_t * stack;
+ fcs_move_t * moves;
+
+ if (instance->solution_moves != NULL)
+ {
+ fcs_move_stack_destroy(instance->solution_moves);
+ instance->solution_moves = NULL;
+ }
+
+ fcs_move_stack_alloc_into_var(solution_moves);
+ instance->solution_moves = solution_moves;
+
+ s1 = instance->final_state;
+
+ /* Retrace the step from the current state to its parents */
+ while (s1->parent != NULL)
+ {
+ /* Mark the state as part of the non-optimized solution */
+ s1->visited |= FCS_VISITED_IN_SOLUTION_PATH;
+ /* Duplicate the move stack */
+ {
+ stack = s1->moves_to_parent;
+ moves = stack->moves;
+ for(move_idx=stack->num_moves-1;move_idx>=0;move_idx--)
+ {
+ fcs_move_stack_push(solution_moves, moves[move_idx]);
+ }
+ }
+ /* Duplicate the state to a freshly malloced memory */
+
+ /* Move to the parent state */
+ s1 = s1->parent;
+ }
+ /* There's one more state than there are move stacks */
+ s1->visited |= FCS_VISITED_IN_SOLUTION_PATH;
+}
+
+
+static fcs_tests_order_t tests_order_dup(fcs_tests_order_t * orig)
+{
+ fcs_tests_order_t ret;
+
+ ret.max_num = ret.num = orig->num;
+ ret.tests = malloc(sizeof(ret.tests[0]) * ret.num);
+ memcpy(ret.tests, orig->tests, sizeof(ret.tests[0]) * ret.num);
+
+ return ret;
+}
+
+/*
+ This function optimizes the solution path using a BFS scan on the
+ states in the solution path.
+*/
+static int freecell_solver_optimize_solution(
+ freecell_solver_instance_t * instance
+ )
+{
+ freecell_solver_hard_thread_t * optimization_thread;
+ freecell_solver_soft_thread_t * soft_thread;
+
+ optimization_thread = alloc_hard_thread(instance);
+ instance->optimization_thread = optimization_thread;
+
+ soft_thread = optimization_thread->soft_threads[0];
+
+ if (instance->opt_tests_order_set)
+ {
+ if (soft_thread->tests_order.tests != NULL)
+ {
+ free(soft_thread->tests_order.tests);
+ }
+
+ soft_thread->tests_order =
+ tests_order_dup(&(instance->opt_tests_order));
+ }
+
+ soft_thread->method = FCS_METHOD_OPTIMIZE;
+
+ soft_thread->is_a_complete_scan = 1;
+
+ /* Initialize the optimization hard-thread and soft-thread */
+ optimization_thread->num_times_left_for_soft_thread = 1000000;
+ freecell_solver_state_ia_init(optimization_thread);
+
+ /* Instruct the optimization hard thread to run indefinitely AFA it
+ * is concerned */
+ optimization_thread->max_num_times = -1;
+ optimization_thread->ht_max_num_times = -1;
+
+ return
+ freecell_solver_a_star_or_bfs_do_solve_or_resume(
+ optimization_thread->soft_threads[0],
+ instance->state_copy_ptr,
+ 0
+ );
+
+}
+
+
+extern void freecell_solver_cache_talon(
+ freecell_solver_instance_t * instance,
+ fcs_state_with_locations_t * new_state
+ );
+
+/*
+ This function starts the solution process _for the first time_. If one
+ wishes to proceed after the iterations limit was reached, one should
+ use freecell_solver_resume_instance.
+
+ */
+int freecell_solver_solve_instance(
+ freecell_solver_instance_t * instance,
+ fcs_state_with_locations_t * init_state
+ )
+{
+ fcs_state_with_locations_t * state_copy_ptr;
+
+ /* Allocate the first state and initialize it to init_state */
+ fcs_state_ia_alloc_into_var(state_copy_ptr, instance->hard_threads[0]);
+
+ fcs_duplicate_state(*state_copy_ptr, *init_state);
+
+ {
+ int a;
+ for(a=0;a<instance->stacks_num;a++)
+ {
+ fcs_copy_stack(*state_copy_ptr, a, instance->hard_threads[0]->indirect_stacks_buffer);
+ }
+ }
+
+ /* Initialize the state to be a base state for the game tree */
+ state_copy_ptr->depth = 0;
+ state_copy_ptr->moves_to_parent = NULL;
+ state_copy_ptr->visited = 0;
+ state_copy_ptr->parent = NULL;
+ memset(&(state_copy_ptr->scan_visited), '\0', sizeof(state_copy_ptr->scan_visited));
+
+ instance->state_copy_ptr = state_copy_ptr;
+
+ /* Initialize the data structure that will manage the state collection */
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE)
+ instance->tree = rbinit(freecell_solver_state_compare_with_context, NULL);
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE)
+ instance->tree = avl_create(freecell_solver_state_compare_with_context, NULL);
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE)
+ instance->tree = rb_create(freecell_solver_state_compare_with_context, NULL);
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_TREE)
+ instance->tree = g_tree_new(freecell_solver_state_compare);
+#endif
+
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH)
+ instance->hash = g_hash_table_new(
+ freecell_solver_hash_function,
+ freecell_solver_state_compare_equal
+ );
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH)
+ instance->hash = freecell_solver_hash_init(
+ 2048,
+ freecell_solver_state_compare_with_context,
+ NULL
+ );
+#endif
+
+ /****************************************************/
+
+#ifdef INDIRECT_STACK_STATES
+ /* Initialize the data structure that will manage the stack
+ collection */
+#if FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH
+ instance->stacks_hash = freecell_solver_hash_init(
+ 2048,
+ fcs_stack_compare_for_comparison_with_context,
+ NULL
+ );
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE)
+ instance->stacks_tree = avl_create(
+ fcs_stack_compare_for_comparison_with_context,
+ NULL
+ );
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE)
+ instance->stacks_tree = rb_create(
+ fcs_stack_compare_for_comparison_with_context,
+ NULL
+ );
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE)
+ instance->stacks_tree = rbinit(
+ fcs_stack_compare_for_comparison_with_context,
+ NULL
+ );
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE)
+ instance->stacks_tree = g_tree_new(fcs_stack_compare_for_comparison);
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH)
+ instance->stacks_hash = g_hash_table_new(
+ freecell_solver_glib_hash_stack_hash_function,
+ freecell_solver_glib_hash_stack_compare
+ );
+#endif
+#endif
+
+ /***********************************************/
+
+#ifdef FCS_WITH_TALONS
+ /* Initialize the Talon's Cache */
+ if (instance->talon_type == FCS_TALON_KLONDIKE)
+ {
+ instance->talons_hash = freecell_solver_hash_init(
+ 512,
+ fcs_talon_compare_with_context,
+ NULL
+ );
+
+ freecell_solver_cache_talon(instance, instance->state_copy_ptr);
+ }
+#endif
+
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_DB_FILE)
+ /* Not working - ignore */
+ db_open(
+ NULL,
+ DB_BTREE,
+ O_CREAT|O_RDWR,
+ 0777,
+ NULL,
+ NULL,
+ &(instance->db)
+ );
+#endif
+
+ {
+ fcs_state_with_locations_t * no_use;
+
+ freecell_solver_check_and_add_state(
+ instance->hard_threads[0]->soft_threads[0],
+ state_copy_ptr,
+ &no_use
+ );
+
+ }
+
+ instance->ht_idx = 0;
+ {
+ int ht_idx;
+ for(ht_idx=0; ht_idx < instance->num_hard_threads ; ht_idx++)
+ {
+ freecell_solver_hard_thread_t * hard_thread;
+ hard_thread = instance->hard_threads[ht_idx];
+
+ if (hard_thread->prelude != NULL)
+ {
+ hard_thread->prelude_idx = 0;
+ hard_thread->st_idx = hard_thread->prelude[hard_thread->prelude_idx].scan_idx;
+ hard_thread->num_times_left_for_soft_thread = hard_thread->prelude[hard_thread->prelude_idx].quota;
+ hard_thread->prelude_idx++;
+ }
+ else
+ {
+ hard_thread->st_idx = 0;
+ }
+ }
+ }
+
+ return freecell_solver_resume_instance(instance);
+}
+
+
+static int run_hard_thread(freecell_solver_hard_thread_t * hard_thread)
+{
+ freecell_solver_soft_thread_t * soft_thread;
+ int num_times_started_at;
+ int ret;
+ freecell_solver_instance_t * instance = hard_thread->instance;
+ /*
+ * Again, making sure that not all of the soft_threads in this
+ * hard thread are finished.
+ * */
+
+ ret = FCS_STATE_SUSPEND_PROCESS;
+ while(hard_thread->num_soft_threads_finished < hard_thread->num_soft_threads)
+ {
+ soft_thread = hard_thread->soft_threads[hard_thread->st_idx];
+ /*
+ * Move to the next thread if it's already finished
+ * */
+ if (soft_thread->is_finished)
+ {
+ /*
+ * Hmmpf - duplicate code. That's ANSI C for you.
+ * A macro, anyone?
+ * */
+
+#define switch_to_next_soft_thread() \
+ /* \
+ * Switch to the next soft thread in the hard thread, \
+ * since we are going to call continue and this is \
+ * a while loop \
+ * */ \
+ if ((hard_thread->prelude != NULL) && \
+ (hard_thread->prelude_idx < hard_thread->prelude_num_items)) \
+ { \
+ hard_thread->st_idx = hard_thread->prelude[hard_thread->prelude_idx].scan_idx; \
+ hard_thread->num_times_left_for_soft_thread = hard_thread->prelude[hard_thread->prelude_idx].quota; \
+ hard_thread->prelude_idx++; \
+ } \
+ else \
+ { \
+ hard_thread->st_idx++; \
+ if (hard_thread->st_idx == hard_thread->num_soft_threads) \
+ { \
+ hard_thread->st_idx = 0; \
+ } \
+ hard_thread->num_times_left_for_soft_thread = hard_thread->soft_threads[hard_thread->st_idx]->num_times_step; \
+ }
+
+
+
+ switch_to_next_soft_thread();
+
+ continue;
+ }
+
+ /*
+ * Keep record of the number of iterations since this
+ * thread started.
+ * */
+ num_times_started_at = hard_thread->num_times;
+ /*
+ * Calculate a soft thread-wise limit for this hard
+ * thread to run.
+ * */
+ hard_thread->max_num_times = hard_thread->num_times + hard_thread->num_times_left_for_soft_thread;
+
+
+
+ /*
+ * Call the resume or solving function that is specific
+ * to each scan
+ *
+ * This switch-like construct calls for declaring a class
+ * that will abstract a scan. But it's not critical since
+ * I don't support user-defined scans.
+ * */
+ switch(soft_thread->method)
+ {
+ case FCS_METHOD_HARD_DFS:
+
+ if (! soft_thread->initialized)
+ {
+ ret = freecell_solver_hard_dfs_solve_for_state(
+ soft_thread,
+ instance->state_copy_ptr,
+ 0,
+ 0);
+
+ soft_thread->initialized = 1;
+ }
+ else
+ {
+ ret = freecell_solver_hard_dfs_resume_solution(soft_thread, 0);
+ }
+ break;
+
+ case FCS_METHOD_SOFT_DFS:
+
+ if (! soft_thread->initialized)
+ {
+ ret =
+ freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume(
+ soft_thread,
+ instance->state_copy_ptr,
+ 0,
+ 0
+ );
+ soft_thread->initialized = 1;
+ }
+ else
+ {
+ ret =
+ freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume(
+ soft_thread,
+ NULL,
+ 1,
+ 0
+ );
+ }
+ break;
+
+ case FCS_METHOD_RANDOM_DFS:
+
+ if (! soft_thread->initialized)
+ {
+ ret =
+ freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume(
+ soft_thread,
+ instance->state_copy_ptr,
+ 0,
+ 1
+ );
+
+ soft_thread->initialized = 1;
+ }
+ else
+ {
+ ret =
+ freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume(
+ soft_thread,
+ NULL,
+ 1,
+ 1
+ );
+ }
+ break;
+
+ case FCS_METHOD_BFS:
+ case FCS_METHOD_A_STAR:
+ case FCS_METHOD_OPTIMIZE:
+ if (! soft_thread->initialized)
+ {
+ if (soft_thread->method == FCS_METHOD_A_STAR)
+ {
+ freecell_solver_a_star_initialize_rater(
+ soft_thread,
+ instance->state_copy_ptr
+ );
+ }
+
+ ret = freecell_solver_a_star_or_bfs_do_solve_or_resume(
+ soft_thread,
+ instance->state_copy_ptr,
+ 0
+ );
+
+ soft_thread->initialized = 1;
+ }
+ else
+ {
+ ret =
+ freecell_solver_a_star_or_bfs_do_solve_or_resume(
+ soft_thread,
+ soft_thread->first_state_to_check,
+ 1
+ );
+ }
+ break;
+
+ default:
+ ret = FCS_STATE_IS_NOT_SOLVEABLE;
+ break;
+ }
+ /*
+ * Determine how much iterations we still have left
+ * */
+ hard_thread->num_times_left_for_soft_thread -= (hard_thread->num_times - num_times_started_at);
+
+ /*
+ * I use <= instead of == because it is possible that
+ * there will be a few more iterations than what this
+ * thread was allocated, due to the fact that
+ * check_and_add_state is only called by the test
+ * functions.
+ *
+ * It's a kludge, but it works.
+ * */
+ if (hard_thread->num_times_left_for_soft_thread <= 0)
+ {
+ switch_to_next_soft_thread();
+ /*
+ * Reset num_times_left_for_soft_thread
+ * */
+
+ }
+
+ /*
+ * It this thread indicated that the scan was finished,
+ * disable the thread or even stop searching altogether.
+ * */
+ if (ret == FCS_STATE_IS_NOT_SOLVEABLE)
+ {
+ soft_thread->is_finished = 1;
+ hard_thread->num_soft_threads_finished++;
+ if (hard_thread->num_soft_threads_finished == hard_thread->num_soft_threads)
+ {
+ instance->num_hard_threads_finished++;
+ }
+ /*
+ * Check if this thread is a complete scan and if so,
+ * terminate the search
+ * */
+ if (soft_thread->is_a_complete_scan)
+ {
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+ }
+ else
+ {
+ /*
+ * Else, make sure ret is something more sensible
+ * */
+ ret = FCS_STATE_SUSPEND_PROCESS;
+ }
+ }
+
+ if ((ret == FCS_STATE_WAS_SOLVED) ||
+ (
+ (ret == FCS_STATE_SUSPEND_PROCESS) &&
+ /* There's a limit to the scan only
+ * if max_num_times is greater than 0 */
+ (
+ (
+ (instance->max_num_times > 0) &&
+ (instance->num_times >= instance->max_num_times)
+ ) ||
+ (
+ (instance->max_num_states_in_collection > 0) &&
+ (instance->num_states_in_collection >= instance->max_num_states_in_collection)
+
+ )
+ )
+ )
+ )
+ {
+ return ret;
+ }
+ else if ((ret == FCS_STATE_SUSPEND_PROCESS) &&
+ (hard_thread->num_times >= hard_thread->ht_max_num_times))
+ {
+ hard_thread->ht_max_num_times += hard_thread->num_times_step;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+
+/* Resume a solution process that was stopped in the middle */
+int freecell_solver_resume_instance(
+ freecell_solver_instance_t * instance
+ )
+{
+ int ret = FCS_STATE_SUSPEND_PROCESS;
+ freecell_solver_hard_thread_t * hard_thread;
+
+ /*
+ * If the optimization thread is defined, it means we are in the
+ * optimization phase of the total scan. In that case, just call
+ * its scanning function.
+ *
+ * Else, proceed with the normal total scan.
+ * */
+ if (instance->optimization_thread)
+ {
+ ret =
+ freecell_solver_a_star_or_bfs_do_solve_or_resume(
+ instance->optimization_thread->soft_threads[0],
+ instance->optimization_thread->soft_threads[0]->first_state_to_check,
+ 1
+ );
+ }
+ else
+ {
+ /*
+ * instance->num_hard_threads_finished signals to us that
+ * all the incomplete soft threads terminated. It is necessary
+ * in case the scan only contains incomplete threads.
+ *
+ * I.e: 01235 and 01246, where no thread contains all tests.
+ * */
+ while(instance->num_hard_threads_finished < instance->num_hard_threads)
+ {
+ /*
+ * A loop on the hard threads.
+ * Note that we do not initialize instance->ht_idx because:
+ * 1. It is initialized before the first call to this function.
+ * 2. It is reset to zero below.
+ * */
+ for(;
+ instance->ht_idx < instance->num_hard_threads ;
+ instance->ht_idx++)
+ {
+ hard_thread = instance->hard_threads[instance->ht_idx];
+
+ ret = run_hard_thread(hard_thread);
+ if ((ret == FCS_STATE_IS_NOT_SOLVEABLE) ||
+ (ret == FCS_STATE_WAS_SOLVED) ||
+ (
+ (ret == FCS_STATE_SUSPEND_PROCESS) &&
+ /* There's a limit to the scan only
+ * if max_num_times is greater than 0 */
+ (
+ (
+ (instance->max_num_times > 0) &&
+ (instance->num_times >= instance->max_num_times)
+ ) ||
+ (
+ (instance->max_num_states_in_collection > 0) &&
+ (instance->num_states_in_collection >= instance->max_num_states_in_collection)
+
+ )
+ )
+ )
+
+ )
+ {
+ goto end_of_hard_threads_loop;
+ }
+ }
+ /*
+ * Avoid over-flow
+ * */
+ if (instance->ht_idx == instance->num_hard_threads)
+ {
+ instance->ht_idx = 0;
+ }
+ }
+
+ end_of_hard_threads_loop:
+
+ /*
+ * If all the incomplete scans finished, then terminate.
+ * */
+ if (instance->num_hard_threads_finished == instance->num_hard_threads)
+ {
+ ret = FCS_STATE_IS_NOT_SOLVEABLE;
+ }
+
+ if (ret == FCS_STATE_WAS_SOLVED)
+ {
+ /* Create solution_moves in the first place */
+ trace_solution(instance);
+ }
+ }
+
+
+ if (ret == FCS_STATE_WAS_SOLVED)
+ {
+ if (instance->optimize_solution_path)
+ {
+ /* Call optimize_solution only once. Make sure that if
+ * it has already run - we retain the old ret. */
+ if (! instance->optimization_thread)
+ {
+ ret = freecell_solver_optimize_solution(instance);
+ }
+ if (ret == FCS_STATE_WAS_SOLVED)
+ {
+ /* Create the solution_moves in the first place */
+ trace_solution(instance);
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+
+/*
+ Clean up a solving process that was terminated in the middle.
+ This function does not substitute for later calling
+ finish_instance() and free_instance().
+ */
+void freecell_solver_unresume_instance(
+ freecell_solver_instance_t * instance
+ )
+{
+ /*
+ * Do nothing - since finish_instance() can take care of solution_states
+ * and proto_solution_moves as they were created by these scans, then
+ * I don't need to do it here, too
+ *
+ * */
+ (void)instance;
+}
+
+
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE) || (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE)
+
+static void freecell_solver_tree_do_nothing(void * data, void * context)
+{
+}
+
+#endif
+
+
+/* A function for freeing a stack for the cleanup of the
+ stacks collection
+*/
+#ifdef INDIRECT_STACK_STATES
+#if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH) || (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE) || (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE)
+#if 0
+static void freecell_solver_stack_free(void * key, void * context)
+{
+ free(key);
+}
+#endif
+
+#elif FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE
+static void freecell_solver_libredblack_walk_destroy_stack_action
+(
+ const void * nodep,
+ const VISIT which,
+ const int depth,
+ void * arg
+ )
+{
+ if ((which == leaf) || (which == preorder))
+ {
+ free((void*)nodep);
+ }
+}
+#elif FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE
+static gint freecell_solver_glib_tree_walk_destroy_stack_action
+(
+ gpointer key,
+ gpointer value,
+ gpointer data
+)
+{
+ free(key);
+
+ return 0;
+}
+
+#elif FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH
+static void freecell_solver_glib_hash_foreach_destroy_stack_action
+(
+ gpointer key,
+ gpointer value,
+ gpointer data
+)
+{
+ free(key);
+}
+#endif
+
+#endif
+
+/***********************************************************/
+
+
+
+
+void freecell_solver_destroy_move_stack_of_state(
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ void * context
+ )
+{
+ (void)context;
+ if (ptr_state_with_locations->moves_to_parent != NULL)
+ {
+ fcs_move_stack_destroy(ptr_state_with_locations->moves_to_parent);
+ }
+}
+
+/*
+ This function should be called after the user has retrieved the
+ results generated by the scan as it will destroy them.
+ */
+void freecell_solver_finish_instance(
+ freecell_solver_instance_t * instance
+ )
+{
+ int ht_idx;
+ freecell_solver_hard_thread_t * hard_thread;
+
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INDIRECT)
+ free(instance->indirect_prev_states);
+#endif
+
+ /* De-allocate the state packs */
+ for(ht_idx=0;ht_idx<instance->num_hard_threads;ht_idx++)
+ {
+ hard_thread = instance->hard_threads[ht_idx];
+ freecell_solver_state_ia_finish(hard_thread);
+
+#ifdef INDIRECT_STACK_STATES
+ freecell_solver_compact_allocator_finish(hard_thread->stacks_allocator);
+ hard_thread->stacks_allocator = NULL;
+#endif
+ freecell_solver_compact_allocator_finish(hard_thread->move_stacks_allocator);
+ hard_thread->move_stacks_allocator = NULL;
+
+ }
+
+ if (instance->optimization_thread)
+ {
+ freecell_solver_state_ia_finish(instance->optimization_thread);
+ }
+
+
+ /* De-allocate the state collection */
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBREDBLACK_TREE)
+ rbdestroy(instance->tree);
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_AVL_TREE)
+ avl_destroy(instance->tree, freecell_solver_tree_do_nothing);
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_LIBAVL_REDBLACK_TREE)
+ rb_destroy(instance->tree, freecell_solver_tree_do_nothing);
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_TREE)
+ g_tree_destroy(instance->tree);
+#endif
+
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GLIB_HASH)
+ g_hash_table_destroy(instance->hash);
+#elif (FCS_STATE_STORAGE == FCS_STATE_STORAGE_INTERNAL_HASH)
+ freecell_solver_hash_free(instance->hash);
+#endif
+
+
+
+ /* De-allocate the stack collection while free()'ing the stacks
+ in the process */
+#ifdef INDIRECT_STACK_STATES
+#if FCS_STACK_STORAGE == FCS_STACK_STORAGE_INTERNAL_HASH
+#if 0
+ freecell_solver_hash_free_with_callback(instance->stacks_hash, freecell_solver_stack_free);
+#else
+ freecell_solver_hash_free(instance->stacks_hash);
+#endif
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_AVL_TREE)
+#if 0
+ avl_destroy(instance->stacks_tree, freecell_solver_stack_free);
+#else
+ avl_destroy(instance->stacks_tree, NULL);
+#endif
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBAVL_REDBLACK_TREE)
+#if 0
+ rb_destroy(instance->stacks_tree, freecell_solver_stack_free);
+#else
+ rb_destroy(instance->stacks_tree, NULL);
+#endif
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_LIBREDBLACK_TREE)
+#if 0
+ rbwalk(instance->stacks_tree,
+ freecell_solver_libredblack_walk_destroy_stack_action,
+ NULL
+ );
+#endif
+ rbdestroy(instance->stacks_tree);
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_TREE)
+#if 0
+ g_tree_traverse(
+ instance->stacks_tree,
+ freecell_solver_glib_tree_walk_destroy_stack_action,
+ G_IN_ORDER,
+ NULL
+ );
+#endif
+ g_tree_destroy(instance->stacks_tree);
+#elif (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GLIB_HASH)
+#if 0
+ g_hash_table_foreach(
+ instance->stacks_hash,
+ freecell_solver_glib_hash_foreach_destroy_stack_action,
+ NULL
+ );
+#endif
+ g_hash_table_destroy(instance->stacks_hash);
+#endif
+#endif
+
+#if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_DB_FILE)
+ instance->db->close(instance->db,0);
+#endif
+
+
+ clean_soft_dfs(instance);
+}
+
+freecell_solver_soft_thread_t * freecell_solver_instance_get_soft_thread(
+ freecell_solver_instance_t * instance,
+ int ht_idx,
+ int st_idx
+ )
+{
+ if (ht_idx >= instance->num_hard_threads)
+ {
+ return NULL;
+ }
+ else
+ {
+ freecell_solver_hard_thread_t * hard_thread;
+ hard_thread = instance->hard_threads[ht_idx];
+ if (st_idx >= hard_thread->num_soft_threads)
+ {
+ return NULL;
+ }
+ else
+ {
+ return hard_thread->soft_threads[st_idx];
+ }
+ }
+}
+
+freecell_solver_soft_thread_t * freecell_solver_new_soft_thread(
+ freecell_solver_soft_thread_t * soft_thread
+ )
+{
+ freecell_solver_soft_thread_t * ret;
+ freecell_solver_hard_thread_t * hard_thread;
+
+ hard_thread = soft_thread->hard_thread;
+ ret = alloc_soft_thread(hard_thread);
+
+ /* Exceeded the maximal number of Soft-Threads in an instance */
+ if (ret == NULL)
+ {
+ return NULL;
+ }
+
+ hard_thread->soft_threads = realloc(hard_thread->soft_threads, sizeof(hard_thread->soft_threads[0])*(hard_thread->num_soft_threads+1));
+ hard_thread->soft_threads[hard_thread->num_soft_threads] = ret;
+ hard_thread->num_soft_threads++;
+
+ return ret;
+}
+
+freecell_solver_soft_thread_t * freecell_solver_new_hard_thread(
+ freecell_solver_instance_t * instance
+ )
+{
+ freecell_solver_hard_thread_t * ret;
+
+ /* Exceeded the maximal number of Soft-Threads in an instance */
+ ret = alloc_hard_thread(instance);
+
+ if (ret == NULL)
+ {
+ return NULL;
+ }
+
+ instance->hard_threads =
+ realloc(
+ instance->hard_threads,
+ (sizeof(instance->hard_threads[0]) * (instance->num_hard_threads+1))
+ );
+
+ instance->hard_threads[instance->num_hard_threads] = ret;
+
+ instance->num_hard_threads++;
+
+ return ret->soft_threads[0];
+}
+
+void freecell_solver_recycle_instance(
+ freecell_solver_instance_t * instance
+ )
+{
+ int ht_idx, st_idx;
+ freecell_solver_hard_thread_t * hard_thread;
+ freecell_solver_soft_thread_t * soft_thread;
+
+ freecell_solver_finish_instance(instance);
+
+ instance->num_times = 0;
+
+ instance->num_hard_threads_finished = 0;
+
+ for(ht_idx = 0; ht_idx < instance->num_hard_threads; ht_idx++)
+ {
+ hard_thread = instance->hard_threads[ht_idx];
+ hard_thread->num_times = 0;
+ hard_thread->ht_max_num_times = hard_thread->num_times_step;
+ hard_thread->max_num_times = -1;
+ hard_thread->num_soft_threads_finished = 0;
+ hard_thread->move_stacks_allocator =
+ freecell_solver_compact_allocator_new();
+#ifdef INDIRECT_STACK_STATES
+ hard_thread->stacks_allocator =
+ freecell_solver_compact_allocator_new();
+#endif
+ for(st_idx = 0; st_idx < hard_thread->num_soft_threads ; st_idx++)
+ {
+ soft_thread = hard_thread->soft_threads[st_idx];
+ soft_thread->is_finished = 0;
+ soft_thread->initialized = 0;
+
+ freecell_solver_rand_srand(soft_thread->rand_gen, soft_thread->rand_seed);
+ /* Reset the priority queue */
+ soft_thread->a_star_pqueue->CurrentSize = 0;
+ }
+ }
+}
diff --git a/kpat/freecell-solver/jhjtypes.h b/kpat/freecell-solver/jhjtypes.h
new file mode 100644
index 00000000..5a98f4c2
--- /dev/null
+++ b/kpat/freecell-solver/jhjtypes.h
@@ -0,0 +1,25 @@
+/*
+ jhjtypes.h - header file for Justin-Heyes Jones' defined types
+
+ Written by Justin-Heyes Jones
+
+ This file is in the public domain (it's uncopyrighted).
+
+ Check out Justin-Heyes Jones' A* page from which this code has
+ originated:
+ http://www.geocities.com/jheyesjones/astar.html
+*/
+
+#ifndef FC_SOLVE__JHJTYPES_H
+#define FC_SOLVE__JHJTYPES_H
+
+/* Data types used in JHeyes-Jones sample code */
+
+typedef int int32;
+typedef unsigned int uint32;
+typedef short int16;
+typedef unsigned short uint16;
+typedef signed char int8;
+typedef unsigned char uint8;
+
+#endif /* #ifdef FC_SOLVE__JHJTYPES_H */
diff --git a/kpat/freecell-solver/lib.c b/kpat/freecell-solver/lib.c
new file mode 100644
index 00000000..1839614b
--- /dev/null
+++ b/kpat/freecell-solver/lib.c
@@ -0,0 +1,1244 @@
+/*
+ * lib.c - library interface functions of Freecell Solver.
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "card.h"
+#include "fcs.h"
+#include "preset.h"
+#include "fcs_user.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+struct fcs_instance_item_struct
+{
+ freecell_solver_instance_t * instance;
+ int ret;
+ int limit;
+};
+
+typedef struct fcs_instance_item_struct fcs_instance_item_t;
+
+struct fcs_user_struct
+{
+ /*
+ * This is a list of several consecutive instances that are run
+ * one after the other in case the previous ones could not solve
+ * the board
+ * */
+ fcs_instance_item_t * instances_list;
+ int num_instances;
+ int max_num_instances;
+
+ int current_instance_idx;
+ /*
+ * The global (sequence-wide) limit of the iterations. Used
+ * by limit_iterations() and friends
+ * */
+ int current_iterations_limit;
+ /*
+ * The number of iterations this board started at.
+ * */
+ int iterations_board_started_at;
+ /*
+ * The number of iterations that the current instance started solving from.
+ * */
+ int init_num_times;
+ /*
+ * A pointer to the currently active instance out of the sequence
+ * */
+ freecell_solver_instance_t * instance;
+ fcs_state_with_locations_t state;
+ fcs_state_with_locations_t running_state;
+ int ret;
+ int state_validity_ret;
+ fcs_card_t state_validity_card;
+ freecell_solver_user_iter_handler_t iter_handler;
+ void * iter_handler_context;
+
+ freecell_solver_soft_thread_t * soft_thread;
+
+#ifdef INDIRECT_STACK_STATES
+ fcs_card_t indirect_stacks_buffer[MAX_NUM_STACKS << 7];
+#endif
+ char * state_string_copy;
+
+ fcs_preset_t common_preset;
+};
+
+typedef struct fcs_user_struct fcs_user_t;
+
+static void user_initialize(
+ fcs_user_t * ret
+ )
+{
+ const fcs_preset_t * freecell_preset;
+
+ freecell_solver_get_preset_by_name(
+ "freecell",
+ &freecell_preset
+ );
+
+ fcs_duplicate_preset(ret->common_preset, *freecell_preset);
+
+ ret->max_num_instances = 10;
+ ret->instances_list = malloc(sizeof(ret->instances_list[0]) * ret->max_num_instances);
+ ret->num_instances = 1;
+ ret->current_instance_idx = 0;
+ ret->instance = freecell_solver_alloc_instance();
+ freecell_solver_apply_preset_by_ptr(ret->instance, &(ret->common_preset));
+ ret->instances_list[ret->current_instance_idx].instance = ret->instance;
+ ret->instances_list[ret->current_instance_idx].ret = ret->ret = FCS_STATE_NOT_BEGAN_YET;
+ ret->instances_list[ret->current_instance_idx].limit = -1;
+ ret->current_iterations_limit = -1;
+
+ ret->soft_thread =
+ freecell_solver_instance_get_soft_thread(
+ ret->instance, 0,0
+ );
+
+ ret->state_string_copy = NULL;
+ ret->iterations_board_started_at = 0;
+}
+
+void * freecell_solver_user_alloc(void)
+{
+ fcs_user_t * ret;
+
+ ret = (fcs_user_t *)malloc(sizeof(fcs_user_t));
+
+ user_initialize(ret);
+
+ return (void*)ret;
+}
+
+int freecell_solver_user_apply_preset(
+ void * user_instance,
+ const char * preset_name)
+{
+ const fcs_preset_t * new_preset_ptr;
+ fcs_user_t * user;
+ int status;
+ int i;
+
+ user = (fcs_user_t*)user_instance;
+
+ status =
+ freecell_solver_get_preset_by_name(
+ preset_name,
+ &new_preset_ptr
+ );
+
+ if (status != FCS_PRESET_CODE_OK)
+ {
+ return status;
+ }
+
+ for(i = 0 ; i < user->num_instances ; i++)
+ {
+ status = freecell_solver_apply_preset_by_ptr(
+ user->instances_list[i].instance,
+ new_preset_ptr
+ );
+
+ if (status != FCS_PRESET_CODE_OK)
+ {
+ return status;
+ }
+ }
+
+ fcs_duplicate_preset(user->common_preset, *new_preset_ptr);
+
+ return FCS_PRESET_CODE_OK;
+}
+
+void freecell_solver_user_limit_iterations(
+ void * user_instance,
+ int max_iters
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t*)user_instance;
+
+ user->current_iterations_limit = max_iters;
+}
+
+void freecell_solver_user_limit_current_instance_iterations(
+ void * user_instance,
+ int max_iters
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t*)user_instance;
+
+ user->instances_list[user->current_instance_idx].limit = max_iters;
+}
+
+#ifndef min
+#define min(a,b) (((a)<(b))?(a):(b))
+#endif
+
+int freecell_solver_user_set_tests_order(
+ void * user_instance,
+ const char * tests_order,
+ char * * error_string
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t*)user_instance;
+
+ return
+ freecell_solver_apply_tests_order(
+ &(user->soft_thread->tests_order),
+ tests_order,
+ error_string
+ );
+}
+
+int freecell_solver_user_solve_board(
+ void * user_instance,
+ const char * state_as_string
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t*)user_instance;
+
+ user->state_string_copy = strdup(state_as_string);
+
+ user->current_instance_idx = 0;
+
+ return freecell_solver_user_resume_solution(user_instance);
+}
+
+static void recycle_instance(
+ fcs_user_t * user,
+ int i
+ )
+{
+ if (user->instances_list[i].ret == FCS_STATE_WAS_SOLVED)
+ {
+ fcs_move_stack_destroy(user->instance->solution_moves);
+ user->instance->solution_moves = NULL;
+ }
+ else if (user->instances_list[i].ret == FCS_STATE_SUSPEND_PROCESS)
+ {
+ freecell_solver_unresume_instance(user->instances_list[i].instance);
+ }
+
+ if (user->instances_list[i].ret != FCS_STATE_NOT_BEGAN_YET)
+ {
+ freecell_solver_recycle_instance(user->instances_list[i].instance);
+ /*
+ * We have to initialize init_num_times to 0 here, because it may not
+ * get initialized again, and now the num_times of the instance
+ * is equal to 0.
+ * */
+ user->init_num_times = 0;
+ }
+
+ user->instances_list[i].ret = FCS_STATE_NOT_BEGAN_YET;
+}
+
+int freecell_solver_user_resume_solution(
+ void * user_instance
+ )
+{
+ int init_num_times;
+ int run_for_first_iteration = 1;
+ int ret;
+ fcs_user_t * user;
+
+ user = (fcs_user_t*)user_instance;
+
+ /*
+ * I expect user->current_instance_idx to be initialized at some value.
+ * */
+ for( ;
+ run_for_first_iteration || ((user->current_instance_idx < user->num_instances) && (ret == FCS_STATE_IS_NOT_SOLVEABLE)) ;
+ recycle_instance(user, user->current_instance_idx), user->current_instance_idx++
+ )
+ {
+ run_for_first_iteration = 0;
+
+ user->instance = user->instances_list[user->current_instance_idx].instance;
+
+ if (user->instances_list[user->current_instance_idx].ret == FCS_STATE_NOT_BEGAN_YET)
+ {
+ int status;
+ status = freecell_solver_initial_user_state_to_c(
+ user->state_string_copy,
+ &(user->state),
+ user->instance->freecells_num,
+ user->instance->stacks_num,
+ user->instance->decks_num
+#ifdef FCS_WITH_TALONS
+ ,user->instance->talon_type
+#endif
+#ifdef INDIRECT_STACK_STATES
+ ,user->indirect_stacks_buffer
+#endif
+ );
+
+ if (status != FCS_USER_STATE_TO_C__SUCCESS)
+ {
+ user->ret = FCS_STATE_INVALID_STATE;
+ user->state_validity_ret = FCS_STATE_VALIDITY__PREMATURE_END_OF_INPUT;
+ return user->ret;
+ }
+
+ user->state_validity_ret = freecell_solver_check_state_validity(
+ &user->state,
+ user->instance->freecells_num,
+ user->instance->stacks_num,
+ user->instance->decks_num,
+#ifdef FCS_WITH_TALONS
+ FCS_TALON_NONE,
+#endif
+ &(user->state_validity_card));
+
+ if (user->state_validity_ret != 0)
+ {
+ user->ret = FCS_STATE_INVALID_STATE;
+ return user->ret;
+ }
+
+
+ /* running_state is a normalized state. So I'm duplicating
+ * state to it before state is canonized
+ * */
+ fcs_duplicate_state(user->running_state, user->state);
+
+ fcs_canonize_state(
+ &user->state,
+ user->instance->freecells_num,
+ user->instance->stacks_num
+ );
+
+ freecell_solver_init_instance(user->instance);
+
+#define global_limit() \
+ (user->instance->num_times + user->current_iterations_limit - user->iterations_board_started_at)
+#define local_limit() \
+ (user->instances_list[user->current_instance_idx].limit)
+#define min(a,b) (((a)<(b))?(a):(b))
+#define calc_max_iters() \
+ { \
+ if (user->instances_list[user->current_instance_idx].limit < 0) \
+ {\
+ if (user->current_iterations_limit < 0)\
+ {\
+ user->instance->max_num_times = -1;\
+ }\
+ else\
+ {\
+ user->instance->max_num_times = global_limit();\
+ }\
+ }\
+ else\
+ {\
+ if (user->current_iterations_limit < 0)\
+ {\
+ user->instance->max_num_times = local_limit();\
+ }\
+ else\
+ {\
+ int a, b;\
+ \
+ a = global_limit();\
+ b = local_limit();\
+ \
+ user->instance->max_num_times = min(a,b);\
+ }\
+ }\
+ }
+
+
+ calc_max_iters();
+
+ user->init_num_times = init_num_times = user->instance->num_times;
+
+ ret = user->ret =
+ user->instances_list[user->current_instance_idx].ret =
+ freecell_solver_solve_instance(user->instance, &user->state);
+ }
+ else
+ {
+
+ calc_max_iters();
+
+ user->init_num_times = init_num_times = user->instance->num_times;
+
+ ret = user->ret =
+ user->instances_list[user->current_instance_idx].ret =
+ freecell_solver_resume_instance(user->instance);
+ }
+
+ user->iterations_board_started_at += user->instance->num_times - init_num_times;
+ user->init_num_times = user->instance->num_times;
+
+ if (user->ret == FCS_STATE_WAS_SOLVED)
+ {
+ freecell_solver_move_stack_normalize(
+ user->instance->solution_moves,
+ &(user->state),
+ user->instance->freecells_num,
+ user->instance->stacks_num,
+ user->instance->decks_num
+ );
+
+ break;
+ }
+ else if (user->ret == FCS_STATE_SUSPEND_PROCESS)
+ {
+ /*
+ * First - check if we exceeded our limit. If so - we must terminate
+ * and return now.
+ * */
+ if ((user->current_iterations_limit >= 0) &&
+ (user->iterations_board_started_at >= user->current_iterations_limit))
+ {
+ break;
+ }
+
+ /*
+ * Determine if we exceeded the instance-specific quota and if
+ * so, designate it as unsolvable.
+ * */
+ if ((local_limit() >= 0) &&
+ (user->instance->num_times >= local_limit())
+ )
+ {
+ ret = FCS_STATE_IS_NOT_SOLVEABLE;
+ }
+ }
+ }
+
+ return ret;
+}
+
+int freecell_solver_user_get_next_move(
+ void * user_instance,
+ fcs_move_t * move
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t*)user_instance;
+ if (user->ret == FCS_STATE_WAS_SOLVED)
+ {
+ int ret;
+
+ ret = fcs_move_stack_pop(
+ user->instance->solution_moves,
+ move
+ );
+
+ if (ret == 0)
+ {
+ freecell_solver_apply_move(
+ &(user->running_state),
+ *move,
+ user->instance->freecells_num,
+ user->instance->stacks_num,
+ user->instance->decks_num
+ );
+ }
+ return ret;
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+char * freecell_solver_user_current_state_as_string(
+ void * user_instance,
+ int parseable_output,
+ int canonized_order_output,
+ int display_10_as_t
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ return
+ freecell_solver_state_as_string(
+ &(user->running_state),
+ user->instance->freecells_num,
+ user->instance->stacks_num,
+ user->instance->decks_num,
+ parseable_output,
+ canonized_order_output,
+ display_10_as_t
+ );
+}
+
+static void user_free_resources(
+ fcs_user_t * user
+ )
+{
+ int i;
+
+ for(i=0;i<user->num_instances;i++)
+ {
+ int ret_code = user->instances_list[i].ret;
+
+ if (ret_code == FCS_STATE_WAS_SOLVED)
+ {
+ fcs_move_stack_destroy(user->instance->solution_moves);
+ user->instance->solution_moves = NULL;
+ }
+ else if (ret_code == FCS_STATE_SUSPEND_PROCESS)
+ {
+ freecell_solver_unresume_instance(user->instances_list[i].instance);
+ }
+
+ if (ret_code != FCS_STATE_NOT_BEGAN_YET)
+ {
+ if (ret_code != FCS_STATE_INVALID_STATE)
+ {
+ freecell_solver_finish_instance(user->instances_list[i].instance);
+ }
+ }
+
+ freecell_solver_free_instance(user->instances_list[i].instance);
+ }
+
+ free(user->instances_list);
+
+ if (user->state_string_copy != NULL)
+ {
+ free(user->state_string_copy);
+ user->state_string_copy = NULL;
+ }
+}
+
+void freecell_solver_user_free(
+ void * user_instance
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ user_free_resources(user);
+
+ free(user);
+}
+
+int freecell_solver_user_get_current_depth(
+ void * user_instance
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ return (user->soft_thread->num_solution_states - 1);
+}
+
+void freecell_solver_user_set_solving_method(
+ void * user_instance,
+ int method
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ user->soft_thread->method = method;
+}
+
+#define set_for_all_instances(what) \
+ { \
+ for(i = 0 ; i < user->num_instances ; i++) \
+ { \
+ user->instances_list[i].instance->what = what; \
+ } \
+ user->common_preset.what = what; \
+ }
+
+int freecell_solver_user_set_num_freecells(
+ void * user_instance,
+ int freecells_num
+ )
+{
+ fcs_user_t * user;
+ int i;
+
+ user = (fcs_user_t *)user_instance;
+
+ if ((freecells_num < 0) || (freecells_num > MAX_NUM_FREECELLS))
+ {
+ return 1;
+ }
+
+ set_for_all_instances(freecells_num);
+
+ return 0;
+}
+
+int freecell_solver_user_set_num_stacks(
+ void * user_instance,
+ int stacks_num
+ )
+{
+ fcs_user_t * user;
+ int i;
+
+ user = (fcs_user_t *)user_instance;
+
+ if ((stacks_num < 0) || (stacks_num > MAX_NUM_STACKS))
+ {
+ return 1;
+ }
+ set_for_all_instances(stacks_num);
+
+ return 0;
+}
+
+int freecell_solver_user_set_num_decks(
+ void * user_instance,
+ int decks_num
+ )
+{
+ fcs_user_t * user;
+ int i;
+
+ user = (fcs_user_t *)user_instance;
+
+ if ((decks_num < 0) || (decks_num > MAX_NUM_DECKS))
+ {
+ return 1;
+ }
+ set_for_all_instances(decks_num);
+
+ return 0;
+}
+
+
+int freecell_solver_user_set_game(
+ void * user_instance,
+ int freecells_num,
+ int stacks_num,
+ int decks_num,
+ int sequences_are_built_by,
+ int unlimited_sequence_move,
+ int empty_stacks_fill
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ if (freecell_solver_user_set_num_freecells(user_instance, freecells_num))
+ {
+ return 1;
+ }
+ if (freecell_solver_user_set_num_stacks(user_instance, stacks_num))
+ {
+ return 2;
+ }
+ if (freecell_solver_user_set_num_decks(user_instance, decks_num))
+ {
+ return 3;
+ }
+ if (freecell_solver_user_set_sequences_are_built_by_type(user_instance, sequences_are_built_by))
+ {
+ return 4;
+ }
+ if (freecell_solver_user_set_sequence_move(user_instance, unlimited_sequence_move))
+ {
+ return 5;
+ }
+ if (freecell_solver_user_set_empty_stacks_filled_by(user_instance, empty_stacks_fill))
+ {
+ return 6;
+ }
+
+ return 0;
+}
+
+int freecell_solver_user_get_num_times(void * user_instance)
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ return user->iterations_board_started_at + user->instance->num_times - user->init_num_times;
+}
+
+int freecell_solver_user_get_limit_iterations(void * user_instance)
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ return user->instance->max_num_times;
+}
+
+int freecell_solver_user_get_moves_left(void * user_instance)
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+ if (user->ret == FCS_STATE_WAS_SOLVED)
+ return user->instance->solution_moves->num_moves;
+ else
+ return 0;
+}
+
+void freecell_solver_user_set_solution_optimization(
+ void * user_instance,
+ int optimize
+)
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ user->instance->optimize_solution_path = optimize;
+}
+
+char * freecell_solver_user_move_to_string(
+ fcs_move_t move,
+ int standard_notation
+ )
+{
+ return freecell_solver_move_to_string(move, standard_notation);
+}
+
+char * freecell_solver_user_move_to_string_w_state(
+ void * user_instance,
+ fcs_move_t move,
+ int standard_notation
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ return
+ freecell_solver_move_to_string_w_state(
+ &(user->running_state),
+ user->instance->freecells_num,
+ user->instance->stacks_num,
+ user->instance->decks_num,
+ move,
+ standard_notation
+ );
+}
+
+void freecell_solver_user_limit_depth(
+ void * user_instance,
+ int max_depth
+)
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ user->instance->max_depth = max_depth;
+}
+
+int freecell_solver_user_get_max_num_freecells(void)
+{
+ return MAX_NUM_FREECELLS;
+}
+
+int freecell_solver_user_get_max_num_stacks(void)
+{
+ return MAX_NUM_STACKS;
+}
+
+int freecell_solver_user_get_max_num_decks(void)
+{
+ return MAX_NUM_DECKS;
+}
+
+
+char * freecell_solver_user_get_invalid_state_error_string(
+ void * user_instance,
+ int print_ts
+ )
+{
+ fcs_user_t * user;
+ char string[80], card_str[10];
+
+ user = (fcs_user_t *)user_instance;
+
+ if (user->state_validity_ret == FCS_STATE_VALIDITY__OK)
+ {
+ return strdup("");
+ }
+ fcs_card_perl2user(user->state_validity_card, card_str, print_ts);
+
+ if (user->state_validity_ret == FCS_STATE_VALIDITY__EMPTY_SLOT)
+ {
+ sprintf(string, "%s",
+ "There's an empty slot in one of the stacks."
+ );
+ }
+ else if ((user->state_validity_ret == FCS_STATE_VALIDITY__EXTRA_CARD) ||
+ (user->state_validity_ret == FCS_STATE_VALIDITY__MISSING_CARD)
+ )
+ {
+ sprintf(string, "%s%s.",
+ ((user->state_validity_ret == FCS_STATE_VALIDITY__EXTRA_CARD)? "There's an extra card: " : "There's a missing card: "),
+ card_str
+ );
+ }
+ else if (user->state_validity_ret == FCS_STATE_VALIDITY__PREMATURE_END_OF_INPUT)
+ {
+ sprintf(string, "%s.", "Not enough input");
+ }
+ return strdup(string);
+}
+
+int freecell_solver_user_set_sequences_are_built_by_type(
+ void * user_instance,
+ int sequences_are_built_by
+ )
+{
+ fcs_user_t * user;
+ int i;
+
+ user = (fcs_user_t *)user_instance;
+
+ if ((sequences_are_built_by < 0) || (sequences_are_built_by > 2))
+ {
+ return 1;
+ }
+ set_for_all_instances(sequences_are_built_by)
+
+ return 0;
+}
+
+int freecell_solver_user_set_sequence_move(
+ void * user_instance,
+ int unlimited_sequence_move
+ )
+{
+ fcs_user_t * user;
+ int i;
+
+ user = (fcs_user_t *)user_instance;
+
+ set_for_all_instances(unlimited_sequence_move);
+
+ return 0;
+}
+
+int freecell_solver_user_set_empty_stacks_filled_by(
+ void * user_instance,
+ int empty_stacks_fill
+ )
+{
+ fcs_user_t * user;
+ int i;
+
+ user = (fcs_user_t *)user_instance;
+
+ if ((empty_stacks_fill < 0) || (empty_stacks_fill > 2))
+ {
+ return 1;
+ }
+ set_for_all_instances(empty_stacks_fill);
+
+ return 0;
+}
+
+int freecell_solver_user_set_a_star_weight(
+ void * user_instance,
+ int index,
+ double weight
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ if ((index < 0) || (index >= (int)(sizeof(user->soft_thread->a_star_weights)/sizeof(user->soft_thread->a_star_weights[0]))))
+ {
+ return 1;
+ }
+ if (weight < 0)
+ {
+ return 2;
+ }
+
+ user->soft_thread->a_star_weights[index] = weight;
+
+ return 0;
+
+}
+
+static void freecell_solver_user_iter_handler_wrapper(
+ void * user_instance,
+ int iter_num,
+ int depth,
+ void * lp_instance,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int parent_iter_num
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ user->iter_handler(
+ user_instance,
+ iter_num,
+ depth,
+ (void *)ptr_state_with_locations,
+ parent_iter_num,
+ user->iter_handler_context
+ );
+
+ (void)lp_instance;
+ return;
+}
+
+void freecell_solver_user_set_iter_handler(
+void * user_instance,
+freecell_solver_user_iter_handler_t iter_handler,
+void * iter_handler_context
+)
+{
+fcs_user_t * user;
+
+user = (fcs_user_t *)user_instance;
+
+if (iter_handler == NULL)
+{
+ user->instance->debug_iter_output = 0;
+}
+else
+{
+ /* Disable it temporarily while we change the settings */
+ user->instance->debug_iter_output = 0;
+ user->iter_handler = iter_handler;
+ user->iter_handler_context = iter_handler_context;
+ user->instance->debug_iter_output_context = user;
+ user->instance->debug_iter_output_func = freecell_solver_user_iter_handler_wrapper;
+ user->instance->debug_iter_output = 1;
+}
+}
+
+char * freecell_solver_user_iter_state_as_string(
+void * user_instance,
+void * ptr_state,
+int parseable_output,
+int canonized_order_output,
+int display_10_as_t
+)
+{
+fcs_user_t * user;
+
+user = (fcs_user_t *)user_instance;
+
+return
+ freecell_solver_state_as_string(
+ ptr_state,
+ user->instance->freecells_num,
+ user->instance->stacks_num,
+ user->instance->decks_num,
+ parseable_output,
+ canonized_order_output,
+ display_10_as_t
+ );
+}
+
+void freecell_solver_user_set_random_seed(
+void * user_instance,
+int seed
+)
+{
+fcs_user_t * user;
+
+user = (fcs_user_t *)user_instance;
+
+freecell_solver_rand_srand(user->soft_thread->rand_gen, (user->soft_thread->rand_seed = seed));
+}
+
+int freecell_solver_user_get_num_states_in_collection(void * user_instance)
+{
+fcs_user_t * user;
+
+user = (fcs_user_t *)user_instance;
+
+return user->instance->num_states_in_collection;
+}
+
+void freecell_solver_user_limit_num_states_in_collection(
+void * user_instance,
+int max_num_states
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t*)user_instance;
+
+ user->instance->max_num_states_in_collection = max_num_states;
+}
+
+int freecell_solver_user_next_soft_thread(
+ void * user_instance
+ )
+{
+ fcs_user_t * user;
+ freecell_solver_soft_thread_t * soft_thread;
+
+ user = (fcs_user_t *)user_instance;
+
+ soft_thread = freecell_solver_new_soft_thread(user->soft_thread);
+
+ if (soft_thread == NULL)
+ {
+ return 1;
+ }
+
+ user->soft_thread = soft_thread;
+
+ return 0;
+}
+
+extern void freecell_solver_user_set_soft_thread_step(
+ void * user_instance,
+ int num_times_step
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ user->soft_thread->num_times_step = num_times_step;
+}
+
+int freecell_solver_user_next_hard_thread(
+ void * user_instance
+ )
+{
+ fcs_user_t * user;
+ freecell_solver_soft_thread_t * soft_thread;
+
+ user = (fcs_user_t *)user_instance;
+
+ soft_thread = freecell_solver_new_hard_thread(user->instance);
+
+ if (soft_thread == NULL)
+ {
+ return 1;
+ }
+
+ user->soft_thread = soft_thread;
+
+ return 0;
+}
+
+int freecell_solver_user_get_num_soft_threads_in_instance(
+ void * user_instance
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ return user->instance->next_soft_thread_id;
+}
+
+void freecell_solver_user_set_calc_real_depth(
+ void * user_instance,
+ int calc_real_depth
+)
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ user->instance->calc_real_depth = calc_real_depth;
+}
+
+void freecell_solver_user_set_soft_thread_name(
+ void * user_instance,
+ char * name
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ if (user->soft_thread->name != NULL)
+ {
+ free(user->soft_thread->name);
+ }
+ user->soft_thread->name = strdup(name);
+}
+
+int freecell_solver_user_set_hard_thread_prelude(
+ void * user_instance,
+ char * prelude
+ )
+{
+ fcs_user_t * user;
+ freecell_solver_hard_thread_t * hard_thread;
+
+ user = (fcs_user_t *)user_instance;
+
+ hard_thread = user->soft_thread->hard_thread;
+
+ if (hard_thread->prelude_as_string != NULL)
+ {
+ free(hard_thread->prelude_as_string);
+ hard_thread->prelude_as_string = NULL;
+ }
+ hard_thread->prelude_as_string = strdup(prelude);
+
+ return 0;
+}
+
+void freecell_solver_user_recycle(
+ void * user_instance
+ )
+{
+ fcs_user_t * user;
+ int i;
+
+ user = (fcs_user_t *)user_instance;
+
+ for(i=0;i<user->num_instances;i++)
+ {
+ recycle_instance(user, i);
+ }
+ user->current_iterations_limit = -1;
+ user->iterations_board_started_at = 0;
+ if (user->state_string_copy != NULL)
+ {
+ free(user->state_string_copy);
+ user->state_string_copy = NULL;
+ }
+}
+
+int freecell_solver_user_set_optimization_scan_tests_order(
+ void * user_instance,
+ const char * tests_order,
+ char * * error_string
+ )
+{
+ fcs_user_t * user;
+ int ret;
+
+ user = (fcs_user_t*)user_instance;
+
+ if (user->instance->opt_tests_order.tests)
+ {
+ free(user->instance->opt_tests_order.tests);
+ user->instance->opt_tests_order.tests = NULL;
+ }
+
+ user->instance->opt_tests_order_set = 0;
+
+ ret =
+ freecell_solver_apply_tests_order(
+ &(user->instance->opt_tests_order),
+ tests_order,
+ error_string
+ );
+
+ if (!ret)
+ {
+ user->instance->opt_tests_order_set = 1;
+ }
+
+ return ret;
+}
+
+void freecell_solver_user_set_reparent_states(
+ void * user_instance,
+ int to_reparent_states
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ user->instance->to_reparent_states = to_reparent_states;
+}
+
+void freecell_solver_user_set_scans_synergy(
+ void * user_instance,
+ int synergy
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ user->instance->scans_synergy = synergy;
+}
+
+int freecell_solver_user_next_instance(
+ void * user_instance
+ )
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ user->num_instances++;
+ if (user->num_instances == user->max_num_instances)
+ {
+ user->max_num_instances += 10;
+ user->instances_list =
+ realloc(
+ user->instances_list,
+ sizeof(user->instances_list[0])*user->max_num_instances
+ );
+ }
+ user->current_instance_idx = user->num_instances-1;
+ user->instance = freecell_solver_alloc_instance();
+
+ freecell_solver_apply_preset_by_ptr(user->instance, &(user->common_preset));
+
+ /*
+ * Switch the soft_thread variable so it won't refer to the old
+ * instance
+ * */
+ user->soft_thread =
+ freecell_solver_instance_get_soft_thread(
+ user->instance, 0, 0
+ );
+
+ user->instances_list[user->current_instance_idx].instance = user->instance;
+ user->instances_list[user->current_instance_idx].ret = user->ret = FCS_STATE_NOT_BEGAN_YET;
+ user->instances_list[user->current_instance_idx].limit = -1;
+
+ return 0;
+}
+
+int freecell_solver_user_reset(void * user_instance)
+{
+ fcs_user_t * user;
+
+ user = (fcs_user_t *)user_instance;
+
+ user_free_resources(user);
+
+ user_initialize(user);
+
+ return 0;
+}
+
diff --git a/kpat/freecell-solver/lookup2.c b/kpat/freecell-solver/lookup2.c
new file mode 100644
index 00000000..6ab9ae7e
--- /dev/null
+++ b/kpat/freecell-solver/lookup2.c
@@ -0,0 +1,119 @@
+/*
+--------------------------------------------------------------------
+lookup2.c, by Bob Jenkins, December 1996, Public Domain.
+hash(), hash2(), hash3, and mix() are externally useful functions.
+Routines to test the hash are included if SELF_TEST is defined.
+You can use this free for any purpose. It has no warranty.
+--------------------------------------------------------------------
+
+Note:
+ This code was ripped and modified by Shlomi Fish. The original can
+ be found at http://burtleburtle.net/bob/c/lookup2.c.
+*/
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "lookup2.h"
+
+
+#define hashsize(n) ((ub4)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+
+/*
+--------------------------------------------------------------------
+mix -- mix 3 32-bit values reversibly.
+For every delta with one or two bit set, and the deltas of all three
+ high bits or all three low bits, whether the original value of a,b,c
+ is almost all zero or is uniformly distributed,
+* If mix() is run forward or backward, at least 32 bits in a,b,c
+ have at least 1/4 probability of changing.
+* If mix() is run forward, every bit of c will change between 1/3 and
+ 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.)
+mix() was built out of 36 single-cycle latency instructions in a
+ structure that could supported 2x parallelism, like so:
+ a -= b;
+ a -= c; x = (c>>13);
+ b -= c; a ^= x;
+ b -= a; x = (a<<8);
+ c -= a; b ^= x;
+ c -= b; x = (b>>13);
+ ...
+ Unfortunately, superscalar Pentiums and Sparcs can't take advantage
+ of that parallelism. They've also turned some of those single-cycle
+ latency instructions into multi-cycle latency instructions. Still,
+ this is the fastest good hash I could find. There were about 2^^68
+ to choose from. I only looked at a billion or so.
+--------------------------------------------------------------------
+*/
+#define mix(a,b,c) \
+{ \
+ a -= b; a -= c; a ^= (c>>13); \
+ b -= c; b -= a; b ^= (a<<8); \
+ c -= a; c -= b; c ^= (b>>13); \
+ a -= b; a -= c; a ^= (c>>12); \
+ b -= c; b -= a; b ^= (a<<16); \
+ c -= a; c -= b; c ^= (b>>5); \
+ a -= b; a -= c; a ^= (c>>3); \
+ b -= c; b -= a; b ^= (a<<10); \
+ c -= a; c -= b; c ^= (b>>15); \
+}
+
+/*
+--------------------------------------------------------------------
+ This works on all machines. hash2() is identical to hash() on
+ little-endian machines, except that the length has to be measured
+ in ub4s instead of bytes. It is much faster than hash(). It
+ requires
+ -- that the key be an array of ub4's, and
+ -- that all your machines have the same endianness, and
+ -- that the length be the number of ub4's in the key
+--------------------------------------------------------------------
+*/
+
+ub4 freecell_solver_lookup2_hash_function(
+ register ub1 *k, /* the key */
+ register ub4 length, /* the length of the key */
+ register ub4 initval /* the previous hash, or an arbitrary value */
+ )
+{
+ register ub4 a,b,c,len;
+
+ /* Set up the internal state */
+ len = length;
+ a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
+ c = initval; /* the previous hash value */
+
+ /*---------------------------------------- handle most of the key */
+ while (len >= 12)
+ {
+ a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24));
+ b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24));
+ c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24));
+ mix(a,b,c);
+ k += 12; len -= 12;
+ }
+
+ /*------------------------------------- handle the last 11 bytes */
+ c += length;
+ switch(len) /* all the case statements fall through */
+ {
+ case 11: c+=((ub4)k[10]<<24);
+ case 10: c+=((ub4)k[9]<<16);
+ case 9 : c+=((ub4)k[8]<<8);
+ /* the first byte of c is reserved for the length */
+ case 8 : b+=((ub4)k[7]<<24);
+ case 7 : b+=((ub4)k[6]<<16);
+ case 6 : b+=((ub4)k[5]<<8);
+ case 5 : b+=k[4];
+ case 4 : a+=((ub4)k[3]<<24);
+ case 3 : a+=((ub4)k[2]<<16);
+ case 2 : a+=((ub4)k[1]<<8);
+ case 1 : a+=k[0];
+ /* case 0: nothing left to add */
+ }
+ mix(a,b,c);
+ /*-------------------------------------------- report the result */
+ return c;
+}
diff --git a/kpat/freecell-solver/lookup2.h b/kpat/freecell-solver/lookup2.h
new file mode 100644
index 00000000..002502ed
--- /dev/null
+++ b/kpat/freecell-solver/lookup2.h
@@ -0,0 +1,13 @@
+#ifndef FC_SOLVE__LOOKUP2_H
+#define FC_SOLVE__LOOKUP2_H
+
+typedef unsigned long int ub4; /* unsigned 4-byte quantities */
+typedef unsigned char ub1;
+
+ub4 freecell_solver_lookup2_hash_function(
+ register ub1 *k, /* the key */
+ register ub4 length, /* the length of the key */
+ register ub4 initval /* the previous hash, or an arbitrary value */
+ );
+
+#endif /* FC_SOLVE__LOOKUP2_H */
diff --git a/kpat/freecell-solver/main.c b/kpat/freecell-solver/main.c
new file mode 100644
index 00000000..d16468c4
--- /dev/null
+++ b/kpat/freecell-solver/main.c
@@ -0,0 +1,859 @@
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+
+#include "fcs_cl.h"
+
+struct freecell_solver_display_information_context_struct
+{
+ int debug_iter_state_output;
+ int freecells_num;
+ int stacks_num;
+ int decks_num;
+ int parseable_output;
+ int canonized_order_output;
+ int display_10_as_t;
+ int display_parent_iter_num;
+ int debug_iter_output_on;
+ int display_moves;
+ int display_states;
+ int standard_notation;
+};
+
+typedef struct freecell_solver_display_information_context_struct freecell_solver_display_information_context_t;
+
+static void init_debug_context(
+ freecell_solver_display_information_context_t * dc
+ )
+{
+ dc->parseable_output = 0;
+ dc->canonized_order_output = 0;
+ dc->display_10_as_t = 0;
+ dc->display_parent_iter_num = 0;
+ dc->display_moves = 0;
+ dc->display_states = 1;
+ dc->standard_notation = 0;
+}
+
+
+
+static void my_iter_handler(
+ void * user_instance,
+ int iter_num,
+ int depth,
+ void * ptr_state,
+ int parent_iter_num,
+ void * lp_context
+ )
+{
+ freecell_solver_display_information_context_t * context;
+ context = (freecell_solver_display_information_context_t*)lp_context;
+
+ fprintf(stdout, "Iteration: %i\n", iter_num);
+ fprintf(stdout, "Depth: %i\n", depth);
+ fprintf(stdout, "Stored-States: %i\n",
+ freecell_solver_user_get_num_states_in_collection(user_instance)
+ );
+ if (context->display_parent_iter_num)
+ {
+ fprintf(stdout, "Parent Iteration: %i\n", parent_iter_num);
+ }
+ fprintf(stdout, "\n");
+
+
+ if (context->debug_iter_state_output)
+ {
+ char * state_string =
+ freecell_solver_user_iter_state_as_string(
+ user_instance,
+ ptr_state,
+ context->parseable_output,
+ context->canonized_order_output,
+ context->display_10_as_t
+ );
+ printf("%s\n---------------\n\n\n", state_string);
+ free((void*)state_string);
+ }
+
+#ifdef MYDEBUG
+ {
+ fcs_card_t card;
+ int ret;
+ char card_str[10];
+
+ ret = fcs_check_state_validity(
+ ptr_state_with_locations,
+ context->freecells_num,
+ context->stacks_num,
+ context->decks_num,
+ &card
+ );
+
+ if (ret != 0)
+ {
+
+ fcs_card_perl2user(card, card_str, context->display_10_as_t);
+ if (ret == 3)
+ {
+ fprintf(stdout, "%s\n",
+ "There's an empty slot in one of the stacks."
+ );
+ }
+ else
+ {
+ fprintf(stdout, "%s%s.\n",
+ ((ret == 2)? "There's an extra card: " : "There's a missing card: "),
+ card_str
+ );
+ }
+ exit(-1);
+ }
+ }
+#endif
+}
+
+struct help_screen_struct
+{
+ char * key;
+ char * screen;
+};
+
+typedef struct help_screen_struct help_screen_t;
+
+help_screen_t help_screens[] = {
+{
+ "configs",
+"These configurations are usually faster than the unmodified run:\n"
+"\n"
+" fc-solve -l cool-jives\n"
+" fc-solve -l john-galt-line\n"
+"\n"
+"Or if you want an accurate verdict:\n"
+"\n"
+" fc-solve -l fools-gold\n"
+"\n"
+"If you want to try constructing your own configurations refer to the\n"
+"USAGE file in the Freecell Solver distribution\n"
+},
+{
+ "options",
+"fc-solve [options] board_file\n"
+"\n"
+"If board_file is - or unspecified reads standard input\n"
+"\n"
+"Available Options:\n"
+"-h --help\n"
+" display the default help screen\n"
+"--help-summary\n"
+" display the summary help screen\n"
+"-i --iter-output\n"
+" display the iteration number and depth in every state that is checked\n"
+"-s --state-output\n"
+" also output the state in every state that is checked\n"
+"-p --parseable-output\n"
+" Output the states in a format that is friendly to perl, grep and\n"
+" friends.\n"
+"-c --canonized-order-output\n"
+" Output the stacks and freecells according to their canonic order.\n"
+" (That means that stacks and freecells won't retain their place.)\n"
+"-t --display-10-as-t\n"
+" Display the card 10 as a capital T instead of \"10\".\n"
+"-m --display-moves\n"
+" Display the moves instead of the intermediate states.\n"
+"-sam --display-states-and-moves \n"
+" Display both intermediate states and moves.\n"
+"-sn --standard-notation\n"
+" Display the moves in standard (non-verbose) notation.\n"
+" (Applicable only if -m was specified)\n"
+"-snx --standard-notation-extended\n"
+" Display the moves in extended standard notation while specifying the\n"
+" number of cards moved if applicable\n"
+"-pi --display-parent-iter \n"
+" Display the index of the parent iteration of each state in the\n"
+" run-time dump.\n"
+"\n"
+"--freecells-num [Freecells\' Number]\n"
+" The number of freecells present in the board.\n"
+"--stacks-num [Stacks\' Number]\n"
+" The number of stacks present in the board.\n"
+"--decks-num [Decks\' Number]\n"
+" The number of decks in the board.\n"
+"\n"
+"--sequences-are-built-by {suit|alternate_color|rank}\n"
+" Specifies the type of sequence\n"
+"--sequence-move {limited|unlimited}\n"
+" Specifies whether the sequence move is limited by the number of\n"
+" freecells or vacant stacks or not.\n"
+"--empty-stacks-filled-by {kings|none|all}\n"
+" Specifies which cards can fill empty stacks.\n"
+"\n"
+"--game [game] --preset [game] -g [game]\n"
+" Specifies the type of game. (Implies several of the game settings\n"
+" options above.). Available presets:\n"
+" bakers_dozen - Baker\'s Dozen\n"
+" bakers_game - Baker\'s Game\n"
+" beleaguered_castle - Beleaguered Castle\n"
+" citadel - Citadel\n"
+" cruel - Cruel\n"
+" der_katz - Der Katzenschwanz\n"
+" die_schlange - Die Schlange\n"
+" eight_off - Eight Off\n"
+" fan - Fan\n"
+" forecell - Forecell\n"
+" freecell - Freecell\n"
+" good_measure - Good Measure\n"
+" ko_bakers_game - Kings\' Only Baker\'s Game\n"
+" relaxed_freecell - Relaxed Freecell\n"
+" relaxed_seahaven - Relaxed Seahaven Towers\n"
+" seahaven - Seahaven Towers\n"
+" simple_simon - Simple Simon\n"
+" streets_and_alleys - Streets and Alleys\n"
+"\n"
+"-md [depth] --max-depth [depth] \n"
+" Specify a maximal search depth for the solution process.\n"
+"-mi [iter_num] --max-iters [iter_num] \n"
+" Specify a maximal number of iterations number.\n"
+"-mss [states_num] --max-stored-states [states_num] \n"
+" Specify the maximal number of states stored in memory.\n"
+"\n"
+"-to [tests_order] --tests-order [tests_order] \n"
+" Specify a test order string. Each test is represented by one character.\n"
+" Valid tests:\n"
+" Freecell Tests:\n"
+"\n"
+" '0' - put top stack cards in the foundations.\n"
+" '1' - put freecell cards in the foundations.\n"
+" '2' - put freecell cards on top of stacks.\n"
+" '3' - put non-top stack cards in the foundations.\n"
+" '4' - move stack cards to different stacks.\n"
+" '5' - move stack cards to a parent card on the same stack.\n"
+" '6' - move sequences of cards onto free stacks.\n"
+" '7' - put freecell cards on empty stacks.\n"
+" '8' - move cards to a different parent.\n"
+" '9' - empty an entire stack into the freecells.\n"
+"\n"
+" Atomic Freecell Tests:\n"
+"\n"
+" 'A' - move a stack card to an empty stack.\n"
+" 'B' - move a stack card to a parent on a different stack.\n"
+" 'C' - move a stack card to a freecell.\n"
+" 'D' - move a freecel card to a parent.\n"
+" 'E' - move a freecel card to an empty stack.\n"
+"\n"
+" Simple Simon Tests:\n"
+"\n"
+" 'a' - move a full sequence to the foundations.\n"
+" 'b' - move a sequence to a true parent of his.\n"
+" 'c' - move a whole stack sequence to a false parent (in order to\n"
+" clear the stack)\n"
+" 'd' - move a sequence to a true parent that has some cards above it.\n"
+" 'e' - move a sequence with some cards above it to a true parent.\n"
+" 'f' - move a sequence with a junk sequence above it to a true parent\n"
+" that has some cards above it.\n"
+" 'g' - move a whole stack sequence to a false parent which has some\n"
+" cards above it.\n"
+" 'h' - move a sequence to a parent on the same stack.\n"
+"\n"
+" Tests are grouped with parenthesis or square brackets. Each group\n"
+" will be randomized as a whole by the random-dfs scan.\n"
+"\n"
+"\n"
+"-me [solving_method] --method [solving_method]\n"
+" Specify a solving method. Available methods are:\n"
+" \"a-star\" - A*\n"
+" \"bfs\" - Breadth-First Search\n"
+" \"dfs\" - Depth-First Search (default)\n"
+" \"random-dfs\" - A randomized DFS\n"
+" \"soft-dfs\" - \"Soft\" DFS\n"
+"\n"
+"-asw [A* Weights] --a-star-weight [A* Weights]\n"
+" Specify weights for the A* scan, assuming it is used. The parameter\n"
+" should be a comma-separated list of numbers, each one is proportional\n"
+" to the weight of its corresponding test.\n"
+"\n"
+" The numbers are, in order:\n"
+" 1. The number of cards out.\n"
+" 2. The maximal sequence move.\n"
+" 3. The number of cards under sequences.\n"
+" 4. The length of the sequences which are found over renegade cards.\n"
+" 5. The depth of the board in the solution.\n"
+"\n"
+"-seed [seed_number]\n"
+" Set the seed for the random number generator used by the\n"
+" \"random-dfs\" scan.\n"
+"\n"
+"-nst --next-soft-thread\n"
+" Move to the next Soft-Thread. I.e: input another scan to run in\n"
+" parallel.\n"
+"-step [step iterations] --soft-thread-step [step iterations]\n"
+" Set the number of iterations in the step of the current soft-thread.\n"
+"-nht --next-hard-thread\n"
+" Move to the next Hard-Thread. This is a new group of scans to run\n"
+" in their own system thread (assuming the executable was compiled with\n"
+" support for them.)\n"
+"--st-name\n"
+" Set the name of the soft-thread.\n"
+"\n"
+"--prelude [prelude_string]\n"
+" Set the prelude string of the hard thread. A prelude is a static\n"
+" sequence of iterations quotas that are executed at the beginning of\n"
+" the search. The format is a list of [Limit]@[Soft-Thread Name]\n"
+" delimited by commas.\n"
+"\n"
+"-ni --next-instance\n"
+" Move to the next distinct solver instance. This is a separate scan\n"
+" which would run only if the previous ones returned an unsolvable\n"
+" verdict.\n"
+"\n"
+"-opt --optimize-solution\n"
+" Try and optimize the solution for a small number of moves.\n"
+"-opt-to --optimization-tests-order\n"
+" The test order of the optimization scan.\n"
+"\n"
+"\n"
+"--reparent-states\n"
+" Reparent states that have a larger depth than that of the state\n"
+" from which they were reached a posteriori.\n"
+"--calc-real-depth\n"
+" If --reparent-states is enabled, then explictly calculate the real\n"
+" depth of a state by tracing its path to the initial state\n"
+"--scans-synergy {none|dead-end-marks}\n"
+" Specifies the cooperation between the scans.\n"
+"\n"
+"\n"
+"--reset\n"
+" Reset the program to its initial, unconfigured state.\n"
+"--read-from-file [{num_skip},]filename\n"
+" Reads configuration parameter with the file while skipping num_skip\n"
+" arguments from the beginning.\n"
+"-l [configuration] --load-config [configuration]\n"
+" Reads the configuration [configruration] and configures the solver\n"
+" accordingly.\n"
+"\n"
+"\n"
+"Signals:\n"
+"SIGUSR1 - Prints the number of states that were checked so far to stderr.\n"
+"SIGUSR2 SIGUSR1 - Turns iteration output on/off.\n"
+"SIGUSR2 SIGUSR2 SIGUSR1 - Turns iteration's state output on/off.\n"
+"\n"
+"\n"
+"Freecell Solver was written by Shlomi Fish.\n"
+"Homepage: http://vipe.technion.ac.il/~shlomif/freecell-solver/\n"
+"Send comments and suggestions to shlomif@vipe.technion.ac.il\n"
+},
+{
+ "real-help",
+"The environment variable FREECELL_SOLVER_DEFAULT_HELP sets the default help\n"
+"screen. The name of the help screen is the same name as its \"--help-\" flag\n"
+"but without the preceding \"--help-\". Type:\n"
+"\n"
+" fc-solve --help-summary\n"
+"\n"
+"for the available help screens.\n"
+"\n"
+"Refer to your system's documentation for information on how to set environment\n"
+"variables.\n"
+},
+{
+ "problems",
+"To be discussed.\n"
+},
+{
+ "short-sol",
+"The following configurations may produce shorter solutions:\n"
+"\n"
+" fc-solve -opt\n"
+" fc-solve --method a-star -opt\n"
+" fc-solve --reparent-states -opt\n"
+" fc-solve --method a-star --reparent-states -opt\n"
+"\n"
+"If \"--method a-star\" is specified you can set the weights with\n"
+"-asw {comma separated list of 5 numeric weights}, which may improve\n"
+"the length of the solution. (refer to the USAGE file for more information)\n"
+},
+{
+ "summary",
+"fc-solve [flags] [board_file|-]\n"
+"\n"
+"Reads board from standard input by default or if a \"-\" is specified.\n"
+"\n"
+"- If it takes too long to finish, type \"fc-solve --help-configs\"\n"
+"- If it erroneously reports a board as unsolvable, try adding the\n"
+" \"-to 01ABCDE\" flag\n"
+"- If the solution is too long type \"fc-solve --help-short-sol\"\n"
+"- To present the moves only try adding \"-m\" or \"-m -snx\"\n"
+"- For a description of all options type \"fc-solve --help-options\"\n"
+"- To deal with other problems type \"fc-solve --help-problems\"\n"
+"- To turn --help into something more useful, type\n"
+" \"fc-solve --help-real-help\"\n"
+"\n"
+"Contact Shlomi Fish, shlomif@vipe.technion.ac.il for more information.\n"
+},
+{
+ NULL,
+ NULL
+}
+}
+;
+
+enum MY_FCS_CMD_LINE_RET_VALUES
+{
+ EXIT_AND_RETURN_0 = FCS_CMD_LINE_USER,
+
+};
+
+static void print_help_string(char * key)
+{
+ int i;
+ for(i=0;help_screens[i].key != NULL ; i++)
+ {
+ if (!strcmp(key, help_screens[i].key))
+ {
+ printf("%s", help_screens[i].screen);
+ }
+ }
+}
+
+static int cmd_line_callback(
+ void * instance,
+ int argc,
+ char * argv[],
+ int arg,
+ int * num_to_skip,
+ int * ret,
+ void * context
+ )
+{
+ freecell_solver_display_information_context_t * dc;
+ *num_to_skip = 0;
+
+ dc = (freecell_solver_display_information_context_t * )context;
+
+ if ((!strcmp(argv[arg], "-h")) || (!strcmp(argv[arg], "--help")))
+ {
+ char * help_key;
+
+ help_key = getenv("FREECELL_SOLVER_DEFAULT_HELP");
+ if (help_key == NULL)
+ {
+ help_key = "summary";
+ }
+ print_help_string(help_key);
+ *ret = EXIT_AND_RETURN_0;
+ return FCS_CMD_LINE_STOP;
+ }
+ else if (!strncmp(argv[arg], "--help-", 7))
+ {
+ print_help_string(argv[arg]+7);
+ *ret = EXIT_AND_RETURN_0;
+ return FCS_CMD_LINE_STOP;
+ }
+ else if ((!strcmp(argv[arg], "-i")) || (!strcmp(argv[arg], "--iter-output")))
+ {
+#define set_iter_handler() \
+ freecell_solver_user_set_iter_handler( \
+ instance, \
+ my_iter_handler, \
+ dc \
+ ); \
+ dc->debug_iter_output_on = 1;
+
+ set_iter_handler();
+ }
+ else if ((!strcmp(argv[arg], "-s")) || (!strcmp(argv[arg], "--state-output")))
+ {
+ set_iter_handler();
+ dc->debug_iter_state_output = 1;
+#undef set_iter_handler
+ }
+ else if ((!strcmp(argv[arg], "-p")) || (!strcmp(argv[arg], "--parseable-output")))
+ {
+ dc->parseable_output = 1;
+ }
+ else if ((!strcmp(argv[arg], "-c")) || (!strcmp(argv[arg], "--canonized-order-output")))
+ {
+ dc->canonized_order_output = 1;
+ }
+ else if ((!strcmp(argv[arg], "-t")) || (!strcmp(argv[arg], "--display-10-as-t")))
+ {
+ dc->display_10_as_t = 1;
+ }
+ else if ((!strcmp(argv[arg], "-m")) || (!strcmp(argv[arg], "--display-moves")))
+ {
+ dc->display_moves = 1;
+ dc->display_states = 0;
+ }
+ else if ((!strcmp(argv[arg], "-sn")) || (!strcmp(argv[arg], "--standard-notation")))
+ {
+ dc->standard_notation = 1;
+
+ }
+ else if ((!strcmp(argv[arg], "-snx")) || (!strcmp(argv[arg], "--standard-notation-extended")))
+ {
+ dc->standard_notation = 2;
+ }
+ else if ((!strcmp(argv[arg], "-sam")) || (!strcmp(argv[arg], "--display-states-and-moves")))
+ {
+ dc->display_moves = 1;
+ dc->display_states = 1;
+ }
+ else if ((!strcmp(argv[arg], "-pi")) || (!strcmp(argv[arg], "--display-parent-iter")))
+ {
+ dc->display_parent_iter_num = 1;
+ }
+ else if ((!strcmp(argv[arg], "--reset")))
+ {
+ init_debug_context(dc);
+ freecell_solver_user_set_iter_handler(
+ instance,
+ NULL,
+ NULL
+ );
+ *num_to_skip = 0;
+ return FCS_CMD_LINE_OK;
+ }
+ else
+ {
+ printf("Unimplemented option - \"%s\"!", argv[arg]);
+ exit(-1);
+ }
+ *num_to_skip = 1;
+ return FCS_CMD_LINE_SKIP;
+}
+
+
+static int command_num = 0;
+static int debug_iter_output_on = 0;
+
+static void select_signal_handler(int signal_num)
+{
+ command_num = (command_num+1)%3;
+}
+
+static void * current_instance;
+static freecell_solver_display_information_context_t * dc;
+
+
+static void command_signal_handler(int signal_num)
+{
+ if (command_num == 0)
+ {
+ fprintf(
+ stderr,
+ "The number of iterations is %i\n",
+ freecell_solver_user_get_num_times(current_instance)
+ );
+ }
+ else if (command_num == 1)
+ {
+ if (debug_iter_output_on)
+ {
+ freecell_solver_user_set_iter_handler(
+ current_instance,
+ NULL,
+ NULL
+ );
+ debug_iter_output_on = 0;
+ }
+ else
+ {
+ freecell_solver_user_set_iter_handler(
+ current_instance,
+ my_iter_handler,
+ dc
+ );
+ debug_iter_output_on = 1;
+ }
+ }
+ else if (command_num == 2)
+ {
+ dc->debug_iter_state_output = ! dc->debug_iter_state_output;
+ }
+
+ command_num = 0;
+}
+
+
+static char * known_parameters[] = {
+ "-h", "--help",
+ "--help-configs", "--help-options", "--help-problems",
+ "--help-real-help", "--help-short-sol", "--help-summary",
+ "-i", "--iter-output",
+ "-s", "--state-output",
+ "-p", "--parseable-output",
+ "-c", "--canonized-order-output",
+ "-t", "--display-10-as-t",
+ "-m", "--display-moves",
+ "-sn", "--standard-notation",
+ "-snx", "--standard-notation-extended",
+ "-sam", "--display-states-and-moves",
+ "-pi", "--display-parent-iter",
+ "--reset",
+ NULL
+ };
+
+#define USER_STATE_SIZE 1024
+
+int main(int argc, char * argv[])
+{
+ int parser_ret;
+ void * instance;
+ char * error_string;
+ int arg;
+ FILE * file;
+ char user_state[USER_STATE_SIZE];
+ int ret;
+
+ freecell_solver_display_information_context_t debug_context;
+
+ init_debug_context(&debug_context);
+
+ dc = &debug_context;
+
+ instance = freecell_solver_user_alloc();
+
+ current_instance = instance;
+
+
+ parser_ret =
+ freecell_solver_user_cmd_line_parse_args(
+ instance,
+ argc,
+ argv,
+ 1,
+ known_parameters,
+ cmd_line_callback,
+ &debug_context,
+ &error_string,
+ &arg
+ );
+
+ if (parser_ret == EXIT_AND_RETURN_0)
+ {
+ freecell_solver_user_free(instance);
+ return 0;
+ }
+ else if (
+ (parser_ret == FCS_CMD_LINE_PARAM_WITH_NO_ARG)
+ )
+ {
+ fprintf(stderr, "The command line parameter \"%s\" requires an argument"
+ " and was not supplied with one.\n", argv[arg]);
+ return (-1);
+ }
+ else if (
+ (parser_ret == FCS_CMD_LINE_ERROR_IN_ARG)
+ )
+ {
+ if (error_string != NULL)
+ {
+ fprintf(stderr, "%s", error_string);
+ free(error_string);
+ }
+ freecell_solver_user_free(instance);
+ return -1;
+ }
+
+ if ((arg == argc) || (!strcmp(argv[arg], "-")))
+ {
+ file = stdin;
+ if (!getenv("FREECELL_SOLVER_QUIET"))
+ {
+ fprintf(stderr, "%s",
+ "Reading the board from the standard input.\n"
+ "Type \"fc-solve --help\" for more usage information.\n"
+ "To cancel this message set the FREECELL_SOLVER_QUIET environment variable.\n"
+ );
+ }
+ }
+ else if (argv[arg][0] == '-')
+ {
+ fprintf(stderr,
+ "Unknown option \"%s\". "
+ "Type \"%s --help\" for usage information.\n",
+ argv[arg],
+ argv[0]
+ );
+ freecell_solver_user_free(instance);
+
+ return -1;
+ }
+ else
+ {
+ file = fopen(argv[arg], "r");
+ if (file == NULL)
+ {
+ fprintf(stderr,
+ "Could not open file \"%s\" for input. Exiting.\n",
+ argv[arg]
+ );
+ freecell_solver_user_free(instance);
+
+ return -1;
+ }
+ }
+ memset(user_state, '\0', sizeof(user_state));
+ fread(user_state, sizeof(user_state[0]), USER_STATE_SIZE-1, file);
+ fclose(file);
+
+ /* Win32 Does not have those signals */
+#ifndef WIN32
+ signal(SIGUSR1, command_signal_handler);
+ signal(SIGUSR2, select_signal_handler);
+#endif
+
+#if 0
+ {
+ int limit = 500;
+ freecell_solver_user_limit_iterations(instance, limit);
+ ret = freecell_solver_user_solve_board(instance, user_state);
+ while (ret == FCS_STATE_SUSPEND_PROCESS)
+ {
+ limit += 500;
+ freecell_solver_user_limit_iterations(instance, limit);
+ ret = freecell_solver_user_resume_solution(instance);
+ }
+ }
+#else
+ ret = freecell_solver_user_solve_board(instance, user_state);
+#endif
+
+ if (ret == FCS_STATE_INVALID_STATE)
+ {
+ char * error_string;
+ error_string =
+ freecell_solver_user_get_invalid_state_error_string(
+ instance,
+ debug_context.display_10_as_t
+ );
+ printf("%s\n", error_string);
+ free(error_string);
+ }
+ else
+ {
+ if (ret == FCS_STATE_WAS_SOLVED)
+ {
+ printf("-=-=-=-=-=-=-=-=-=-=-=-\n\n");
+ {
+ fcs_move_t move;
+ FILE * move_dump;
+ char * as_string;
+ int move_num = 0;
+
+ move_dump = stdout;
+
+
+ if (debug_context.display_states)
+ {
+ as_string =
+ freecell_solver_user_current_state_as_string(
+ instance,
+ debug_context.parseable_output,
+ debug_context.canonized_order_output,
+ debug_context.display_10_as_t
+ );
+
+ fprintf(move_dump, "%s\n", as_string);
+
+ free(as_string);
+
+ fprintf(move_dump, "%s", "\n====================\n\n");
+ }
+
+ while (
+ freecell_solver_user_get_next_move(
+ instance,
+ &move
+ ) == 0
+ )
+ {
+ if (debug_context.display_moves)
+ {
+ as_string =
+ freecell_solver_user_move_to_string_w_state(
+ instance,
+ move,
+ debug_context.standard_notation
+ );
+
+ if (debug_context.display_states && debug_context.standard_notation)
+ {
+ fprintf(move_dump, "Move: ");
+ }
+
+ fprintf(
+ move_dump,
+ (debug_context.standard_notation ?
+ "%s " :
+ "%s\n"
+ ),
+ as_string
+ );
+ move_num++;
+ if (debug_context.standard_notation)
+ {
+ if ((move_num % 10 == 0) || debug_context.display_states)
+ {
+ fprintf(move_dump, "\n");
+ }
+ }
+ if (debug_context.display_states)
+ {
+ fprintf(move_dump, "\n");
+ }
+ fflush(move_dump);
+ free(as_string);
+ }
+
+ if (debug_context.display_states)
+ {
+ as_string =
+ freecell_solver_user_current_state_as_string(
+ instance,
+ debug_context.parseable_output,
+ debug_context.canonized_order_output,
+ debug_context.display_10_as_t
+ );
+
+ fprintf(move_dump, "%s\n", as_string);
+
+ free(as_string);
+ }
+
+ if (debug_context.display_states || (!debug_context.standard_notation))
+ {
+ fprintf(move_dump, "%s", "\n====================\n\n");
+ }
+ }
+
+ if (debug_context.standard_notation && (!debug_context.display_states))
+ {
+ fprintf(move_dump, "\n\n");
+ }
+ }
+
+ printf("This game is solveable.\n");
+ }
+ else
+ {
+ printf ("I could not solve this game.\n");
+ }
+
+ printf(
+ "Total number of states checked is %i.\n",
+ freecell_solver_user_get_num_times(instance)
+ );
+#if 1
+ printf(
+ "This scan generated %i states.\n",
+ freecell_solver_user_get_num_states_in_collection(instance)
+ );
+#endif
+ }
+
+ freecell_solver_user_free(instance);
+
+ return 0;
+}
+
diff --git a/kpat/freecell-solver/move.c b/kpat/freecell-solver/move.c
new file mode 100644
index 00000000..aa8ed560
--- /dev/null
+++ b/kpat/freecell-solver/move.c
@@ -0,0 +1,531 @@
+/*
+ * move.c - move and move stacks routines for Freecell Solver
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "move.h"
+#include "state.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+#include "inline.h"
+
+#if 0
+/* This variable was used for debugging. */
+int msc_counter=0;
+#endif
+
+#if 0
+/* This function allocates an empty move stack */
+fcs_move_stack_t * fcs_move_stack_create(void)
+{
+ fcs_move_stack_t * ret;
+
+ /* Allocate the data structure itself */
+ ret = (fcs_move_stack_t *)malloc(sizeof(fcs_move_stack_t));
+
+ ret->max_num_moves = FCS_MOVE_STACK_GROW_BY;
+ ret->num_moves = 0;
+ /* Allocate some space for the moves */
+ ret->moves = (fcs_move_t *)malloc(sizeof(fcs_move_t)*ret->max_num_moves);
+
+ return ret;
+}
+#endif
+
+#if 0
+int fcs_move_stack_push(fcs_move_stack_t * stack, fcs_move_t move)
+{
+ /* If all the moves inside the stack are taken then
+ resize the move vector */
+
+ if (stack->num_moves == stack->max_num_moves)
+ {
+ int a, b;
+ a = (stack->max_num_moves >> 3);
+ b = FCS_MOVE_STACK_GROW_BY;
+ stack->max_num_moves += max(a,b);
+ stack->moves = realloc(
+ stack->moves,
+ stack->max_num_moves * sizeof(fcs_move_t)
+ );
+ }
+ stack->moves[stack->num_moves++] = move;
+
+ return 0;
+}
+#endif
+
+int freecell_solver_move_stack_pop(fcs_move_stack_t * stack, fcs_move_t * move)
+{
+ if (stack->num_moves > 0)
+ {
+ *move = stack->moves[--stack->num_moves];
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+#if 0
+void fcs_move_stack_destroy(fcs_move_stack_t * stack)
+{
+ free(stack->moves);
+ free(stack);
+}
+#endif
+
+void freecell_solver_move_stack_swallow_stack(
+ fcs_move_stack_t * stack,
+ fcs_move_stack_t * src_stack
+ )
+{
+ fcs_move_t move;
+ while (!fcs_move_stack_pop(src_stack, &move))
+ {
+ fcs_move_stack_push(stack, move);
+ }
+ fcs_move_stack_destroy(src_stack);
+}
+
+#if 0
+void fcs_move_stack_reset(
+ fcs_move_stack_t * stack
+ )
+{
+ stack->num_moves = 0;
+}
+#endif
+
+int freecell_solver_move_stack_get_num_moves(
+ fcs_move_stack_t * stack
+ )
+{
+ return stack->num_moves;
+}
+
+#if 0
+/*
+ This function duplicates a move stack
+*/
+fcs_move_stack_t * fcs_move_stack_duplicate(
+ fcs_move_stack_t * stack
+ )
+{
+ fcs_move_stack_t * ret;
+
+ ret = (fcs_move_stack_t *)malloc(sizeof(fcs_move_stack_t));
+
+ ret->max_num_moves = stack->max_num_moves;
+ ret->num_moves = stack->num_moves;
+ ret->moves = (fcs_move_t *)malloc(sizeof(fcs_move_t) * ret->max_num_moves);
+ memcpy(ret->moves, stack->moves, sizeof(fcs_move_t) * ret->max_num_moves);
+
+ return ret;
+}
+#endif
+
+#if 0
+extern void fcs_derived_states_list_add_state(
+ fcs_derived_states_list_t * list,
+ fcs_state_with_locations_t * state,
+ fcs_move_stack_t * move_stack
+ )
+{
+ if (list->num_states == list->max_num_states)
+ {
+ list->max_num_states += 16;
+ list->states = realloc(list->states, sizeof(list->states[0]) * list->max_num_states);
+ list->move_stacks = realloc(list->move_stacks, sizeof(list->move_stacks[0]) * list->max_num_states);
+ }
+ list->states[list->num_states] = state;
+ list->move_stacks[list->num_states] = move_stack;
+ list->num_states++;
+}
+#endif
+/*
+ This function performs a given move on a state
+
+ */
+void freecell_solver_apply_move(fcs_state_with_locations_t * state_with_locations, fcs_move_t move, int freecells_num, int stacks_num, int decks_num)
+{
+ fcs_state_t * state;
+ fcs_card_t temp_card;
+ int a;
+ int src_stack, dest_stack;
+ int src_freecell, dest_freecell;
+ int src_stack_len;
+
+ state = (&(state_with_locations->s));
+
+ dest_stack = fcs_move_get_dest_stack(move);
+ src_stack = fcs_move_get_src_stack(move);
+ dest_freecell = fcs_move_get_dest_freecell(move);
+ src_freecell = fcs_move_get_src_freecell(move);
+
+
+ switch(fcs_move_get_type(move))
+ {
+ case FCS_MOVE_TYPE_STACK_TO_STACK:
+ {
+ src_stack_len = fcs_stack_len(*state, src_stack);
+ for(a=0 ; a<fcs_move_get_num_cards_in_seq(move) ; a++)
+ {
+ fcs_push_stack_card_into_stack(
+ *state,
+ dest_stack,
+ src_stack,
+ src_stack_len - fcs_move_get_num_cards_in_seq(move)+a
+ );
+ }
+ for(a=0 ; a<fcs_move_get_num_cards_in_seq(move) ; a++)
+ {
+ fcs_pop_stack_card(
+ *state,
+ src_stack,
+ temp_card
+ );
+ }
+ }
+ break;
+ case FCS_MOVE_TYPE_FREECELL_TO_STACK:
+ {
+ temp_card = fcs_freecell_card(*state, src_freecell);
+ fcs_push_card_into_stack(*state, dest_stack, temp_card);
+ fcs_empty_freecell(*state, src_freecell);
+ }
+ break;
+ case FCS_MOVE_TYPE_FREECELL_TO_FREECELL:
+ {
+ temp_card = fcs_freecell_card(*state, src_freecell);
+ fcs_put_card_in_freecell(*state, dest_freecell, temp_card);
+ fcs_empty_freecell(*state, src_freecell);
+ }
+ break;
+ case FCS_MOVE_TYPE_STACK_TO_FREECELL:
+ {
+ fcs_pop_stack_card(*state, src_stack, temp_card);
+ fcs_put_card_in_freecell(*state, dest_freecell, temp_card);
+ }
+ break;
+ case FCS_MOVE_TYPE_STACK_TO_FOUNDATION:
+ {
+ fcs_pop_stack_card(*state, src_stack, temp_card);
+ fcs_increment_foundation(*state, fcs_move_get_foundation(move));
+ }
+ break;
+ case FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION:
+ {
+ fcs_empty_freecell(*state, src_freecell);
+ fcs_increment_foundation(*state, fcs_move_get_foundation(move));
+ }
+ break;
+ case FCS_MOVE_TYPE_SEQ_TO_FOUNDATION:
+ {
+ for(a=0;a<13;a++)
+ {
+ fcs_pop_stack_card(*state, src_stack, temp_card);
+ fcs_increment_foundation(*state, fcs_move_get_foundation(move));
+ }
+ }
+ break;
+
+ case FCS_MOVE_TYPE_FLIP_CARD:
+ {
+ fcs_flip_stack_card(*state, src_stack, fcs_stack_len(*state, src_stack)-1);
+ }
+ break;
+
+ case FCS_MOVE_TYPE_CANONIZE:
+ {
+ fcs_canonize_state(state_with_locations, freecells_num, stacks_num);
+ }
+ break;
+
+ }
+}
+
+/*
+ The purpose of this function is to convert the moves from using
+ the canonized positions of the stacks and freecells to their
+ user positions.
+*/
+
+void freecell_solver_move_stack_normalize(
+ fcs_move_stack_t * moves,
+ fcs_state_with_locations_t * init_state,
+ int freecells_num,
+ int stacks_num,
+ int decks_num
+ )
+{
+ fcs_move_stack_t * temp_moves;
+ fcs_move_t in_move, out_move;
+ fcs_state_with_locations_t dynamic_state;
+#ifdef INDIRECT_STACK_STATES
+ char buffer[MAX_NUM_STACKS << 7];
+ int a;
+#endif
+
+ fcs_move_init(out_move);
+ fcs_move_stack_alloc_into_var(temp_moves);
+
+ fcs_duplicate_state(dynamic_state, *init_state);
+#ifdef INDIRECT_STACK_STATES
+ for(a=0;a<stacks_num;a++)
+ {
+ fcs_copy_stack(dynamic_state, a, buffer);
+ }
+#endif
+
+ while (
+ fcs_move_stack_pop(
+ moves,
+ &in_move
+ ) == 0)
+ {
+ freecell_solver_apply_move(
+ &dynamic_state,
+ in_move,
+ freecells_num,
+ stacks_num,
+ decks_num
+ );
+ if (fcs_move_get_type(in_move) == FCS_MOVE_TYPE_CANONIZE)
+ {
+ /* Do Nothing */
+ }
+ else
+ {
+ fcs_move_set_type(out_move, fcs_move_get_type(in_move));
+ if ((fcs_move_get_type(in_move) == FCS_MOVE_TYPE_STACK_TO_STACK) ||
+ (fcs_move_get_type(in_move) == FCS_MOVE_TYPE_STACK_TO_FREECELL) ||
+ (fcs_move_get_type(in_move) == FCS_MOVE_TYPE_STACK_TO_FOUNDATION) ||
+ (fcs_move_get_type(in_move) == FCS_MOVE_TYPE_SEQ_TO_FOUNDATION)
+ )
+ {
+ fcs_move_set_src_stack(out_move,dynamic_state.stack_locs[(int)fcs_move_get_src_stack(in_move)]);
+ }
+
+ if (
+ (fcs_move_get_type(in_move) == FCS_MOVE_TYPE_FREECELL_TO_STACK) ||
+ (fcs_move_get_type(in_move) == FCS_MOVE_TYPE_FREECELL_TO_FREECELL) ||
+ (fcs_move_get_type(in_move) == FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION))
+ {
+ fcs_move_set_src_freecell(out_move,dynamic_state.fc_locs[(int)fcs_move_get_src_freecell(in_move)]);
+ }
+
+ if (
+ (fcs_move_get_type(in_move) == FCS_MOVE_TYPE_STACK_TO_STACK) ||
+ (fcs_move_get_type(in_move) == FCS_MOVE_TYPE_FREECELL_TO_STACK)
+ )
+ {
+ fcs_move_set_dest_stack(out_move,dynamic_state.stack_locs[(int)fcs_move_get_dest_stack(in_move)]);
+ }
+
+ if (
+ (fcs_move_get_type(in_move) == FCS_MOVE_TYPE_STACK_TO_FREECELL) ||
+ (fcs_move_get_type(in_move) == FCS_MOVE_TYPE_FREECELL_TO_FREECELL)
+ )
+ {
+ fcs_move_set_dest_freecell(out_move,dynamic_state.fc_locs[(int)fcs_move_get_dest_freecell(in_move)]);
+ }
+
+ if ((fcs_move_get_type(in_move) == FCS_MOVE_TYPE_STACK_TO_FOUNDATION) ||
+ (fcs_move_get_type(in_move) == FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION) ||
+ (fcs_move_get_type(in_move) == FCS_MOVE_TYPE_SEQ_TO_FOUNDATION)
+
+ )
+ {
+ fcs_move_set_foundation(out_move,fcs_move_get_foundation(in_move));
+ }
+
+ if ((fcs_move_get_type(in_move) == FCS_MOVE_TYPE_STACK_TO_STACK))
+ {
+ fcs_move_set_num_cards_in_seq(out_move,fcs_move_get_num_cards_in_seq(in_move));
+ }
+
+ fcs_move_stack_push(temp_moves, out_move);
+ }
+ }
+
+ /*
+ * temp_moves contain the needed moves in reverse order. So let's use
+ * swallow_stack to put them in the original in the correct order.
+ *
+ * */
+ fcs_move_stack_reset(moves);
+
+ freecell_solver_move_stack_swallow_stack(moves, temp_moves);
+}
+
+GCC_INLINE int convert_freecell_num(int fcn)
+{
+ if (fcn >= 7)
+ return (fcn+3);
+ else
+ return fcn;
+}
+
+char * freecell_solver_move_to_string(fcs_move_t move, int standard_notation)
+{
+ return
+ freecell_solver_move_to_string_w_state(
+ NULL, 4, 8, 1,
+ move,
+ (standard_notation == 2)?1:standard_notation
+ );
+}
+
+char * freecell_solver_move_to_string_w_state(fcs_state_with_locations_t * state, int freecells_num, int stacks_num, int decks_num, fcs_move_t move, int standard_notation)
+{
+ char string[256];
+ switch(fcs_move_get_type(move))
+ {
+ case FCS_MOVE_TYPE_STACK_TO_STACK:
+ if ((standard_notation == 2) &&
+ /* More than one card was moved */
+ (fcs_move_get_num_cards_in_seq(move) > 1) &&
+ /* It was a move to an empty stack */
+ (fcs_stack_len(state->s, fcs_move_get_dest_stack(move)) ==
+ fcs_move_get_num_cards_in_seq(move))
+ )
+ {
+ sprintf(string, "%i%iv%x",
+ 1+fcs_move_get_src_stack(move),
+ 1+fcs_move_get_dest_stack(move),
+ fcs_move_get_num_cards_in_seq(move)
+ );
+ }
+ else if (standard_notation)
+ {
+ sprintf(string, "%i%i",
+ 1+fcs_move_get_src_stack(move),
+ 1+fcs_move_get_dest_stack(move)
+ );
+ }
+ else
+ {
+ sprintf(string, "Move %i cards from stack %i to stack %i",
+ fcs_move_get_num_cards_in_seq(move),
+ fcs_move_get_src_stack(move),
+ fcs_move_get_dest_stack(move)
+ );
+ }
+ break;
+
+ case FCS_MOVE_TYPE_FREECELL_TO_STACK:
+ if (standard_notation)
+ {
+ sprintf(string, "%c%i",
+ ('a'+convert_freecell_num(fcs_move_get_src_freecell(move))),
+ 1+fcs_move_get_dest_stack(move)
+ );
+ }
+ else
+ {
+ sprintf(string, "Move a card from freecell %i to stack %i",
+ fcs_move_get_src_freecell(move),
+ fcs_move_get_dest_stack(move)
+ );
+ }
+
+ break;
+
+ case FCS_MOVE_TYPE_FREECELL_TO_FREECELL:
+ if (standard_notation)
+ {
+ sprintf(string, "%c%c",
+ ('a'+convert_freecell_num(fcs_move_get_src_freecell(move))),
+ ('a'+convert_freecell_num(fcs_move_get_dest_freecell(move)))
+ );
+ }
+ else
+ {
+ sprintf(string, "Move a card from freecell %i to freecell %i",
+ fcs_move_get_src_freecell(move),
+ fcs_move_get_dest_freecell(move)
+ );
+ }
+
+ break;
+
+ case FCS_MOVE_TYPE_STACK_TO_FREECELL:
+ if (standard_notation)
+ {
+ sprintf(string, "%i%c",
+ 1+fcs_move_get_src_stack(move),
+ ('a'+convert_freecell_num(fcs_move_get_dest_freecell(move)))
+ );
+ }
+ else
+ {
+ sprintf(string, "Move a card from stack %i to freecell %i",
+ fcs_move_get_src_stack(move),
+ fcs_move_get_dest_freecell(move)
+ );
+ }
+
+ break;
+
+ case FCS_MOVE_TYPE_STACK_TO_FOUNDATION:
+ if (standard_notation)
+ {
+ sprintf(string, "%ih", 1+fcs_move_get_src_stack(move));
+ }
+ else
+ {
+ sprintf(string, "Move a card from stack %i to the foundations",
+ fcs_move_get_src_stack(move)
+ );
+ }
+
+ break;
+
+
+ case FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION:
+ if (standard_notation)
+ {
+ sprintf(string, "%ch", ('a'+convert_freecell_num(fcs_move_get_src_freecell(move))));
+ }
+ else
+ {
+ sprintf(string,
+ "Move a card from freecell %i to the foundations",
+ fcs_move_get_src_freecell(move)
+ );
+ }
+
+ break;
+
+ case FCS_MOVE_TYPE_SEQ_TO_FOUNDATION:
+ if (standard_notation)
+ {
+ sprintf(string, "%ih", fcs_move_get_src_stack(move));
+ }
+ else
+ {
+ sprintf(string,
+ "Move the sequence on top of Stack %i to the foundations",
+ fcs_move_get_src_stack(move)
+ );
+ }
+ break;
+
+ default:
+ string[0] = '\0';
+ break;
+ }
+
+ return strdup(string);
+}
diff --git a/kpat/freecell-solver/move.h b/kpat/freecell-solver/move.h
new file mode 100644
index 00000000..a7501788
--- /dev/null
+++ b/kpat/freecell-solver/move.h
@@ -0,0 +1,172 @@
+/*
+ * move.h - header file for the move and move stacks functions of
+ * Freecell Solver
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+
+#ifndef FC_SOLVE__MOVE_H
+#define FC_SOLVE__MOVE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This include is done to prevent a warning in case stdlib.h defines
+ * max. (which is the case for the Microsoft C Compiler)
+ * */
+#include <stdlib.h>
+
+#include "state.h"
+#include "fcs_move.h"
+
+
+#if 0
+fcs_move_stack_t * fcs_move_stack_create(void);
+int fcs_move_stack_push(fcs_move_stack_t * stack, fcs_move_t move);
+#endif
+
+#define fcs_move_stack_pop(stack,move) (freecell_solver_move_stack_pop(stack,move))
+extern int freecell_solver_move_stack_pop(fcs_move_stack_t * stack, fcs_move_t * move);
+
+#if 0
+void fcs_move_stack_destroy(fcs_move_stack_t * stack);
+#endif
+
+#define fcs_move_stack_destroy(stack) \
+{ \
+ free((stack)->moves); \
+ free(stack); \
+}
+
+extern void freecell_solver_move_stack_swallow_stack(fcs_move_stack_t * stack, fcs_move_stack_t * src_stack);
+#if 0
+void fcs_move_stack_reset(fcs_move_stack_t * stack);
+#endif
+#define fcs_move_stack_reset(stack) \
+{ \
+ (stack)->num_moves = 0; \
+}
+
+
+
+#define fcs_move_stack_get_num_moves(stack) (freecell_solver_move_stack_get_num_moves(stack))
+extern int freecell_solver_move_stack_get_num_moves(fcs_move_stack_t * stack);
+
+#if 0
+fcs_move_stack_t * fcs_move_stack_duplicate(fcs_move_stack_t * stack);
+#endif
+#define fcs_move_stack_duplicate_into_var(final_ret,stack) \
+{ \
+ fcs_move_stack_t * ret; \
+ fcs_move_stack_t * temp_stack=(stack) ; \
+ \
+ ret = (fcs_move_stack_t *)malloc(sizeof(fcs_move_stack_t)); \
+ \
+ ret->max_num_moves = temp_stack->max_num_moves; \
+ ret->num_moves = temp_stack->num_moves; \
+ ret->moves = (fcs_move_t *)malloc(sizeof(fcs_move_t) * ret->max_num_moves); \
+ memcpy(ret->moves, temp_stack->moves, sizeof(fcs_move_t) * ret->max_num_moves); \
+ \
+ (final_ret) = ret; \
+}
+
+
+
+void freecell_solver_apply_move(fcs_state_with_locations_t * state_with_locations, fcs_move_t move, int freecells_num, int stacks_num, int decks_num);
+
+void freecell_solver_move_stack_normalize(
+ fcs_move_stack_t * moves,
+ fcs_state_with_locations_t * init_state,
+ int freecells_num,
+ int stacks_num,
+ int decks_num
+ );
+
+extern char * freecell_solver_move_to_string(fcs_move_t move, int standard_notation);
+
+extern char * freecell_solver_move_to_string_w_state(fcs_state_with_locations_t * state, int freecells_num, int stacks_num, int decks_num, fcs_move_t move, int standard_notation);
+
+struct fcs_derived_states_list_struct
+{
+ int num_states;
+ int max_num_states;
+ fcs_state_with_locations_t * * states;
+};
+
+typedef struct fcs_derived_states_list_struct fcs_derived_states_list_t;
+
+#if 0
+extern void fcs_derived_states_list_add_state(
+ fcs_derived_states_list_t * list,
+ fcs_state_with_locations_t * state,
+ fcs_move_stack_t * move_stack
+ );
+#endif
+
+#ifndef max
+#define max(a,b) (((a)>(b))?(a):(b))
+#endif
+
+#define FCS_MOVE_STACK_GROW_BY 16
+
+/* This macro allocates an empty move stack */
+#define fcs_move_stack_alloc_into_var(final_ret) \
+{ \
+ fcs_move_stack_t * ret; \
+ \
+ /* Allocate the data structure itself */ \
+ ret = (fcs_move_stack_t *)malloc(sizeof(fcs_move_stack_t)); \
+ \
+ ret->max_num_moves = FCS_MOVE_STACK_GROW_BY; \
+ ret->num_moves = 0; \
+ /* Allocate some space for the moves */ \
+ ret->moves = (fcs_move_t *)malloc(sizeof(fcs_move_t)*ret->max_num_moves); \
+ \
+ (final_ret) = ret; \
+}
+
+
+#define fcs_move_stack_push(stack, move) \
+{ \
+ /* If all the moves inside the stack are taken then \
+ resize the move vector */ \
+ \
+ if (stack->num_moves == stack->max_num_moves) \
+ { \
+ int a, b; \
+ a = (stack->max_num_moves >> 3); \
+ b = FCS_MOVE_STACK_GROW_BY; \
+ stack->max_num_moves += max(a,b); \
+ stack->moves = realloc( \
+ stack->moves, \
+ stack->max_num_moves * sizeof(fcs_move_t) \
+ ); \
+ } \
+ stack->moves[stack->num_moves++] = move; \
+ \
+}
+
+#define fcs_derived_states_list_add_state(list,state) \
+ \
+{ \
+ if ((list)->num_states == (list)->max_num_states) \
+ { \
+ (list)->max_num_states += 16; \
+ (list)->states = realloc((list)->states, sizeof((list)->states[0]) * (list)->max_num_states); \
+ } \
+ (list)->states[(list)->num_states] = (state); \
+ (list)->num_states++; \
+}
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FC_SOLVE__MOVE_H */
diff --git a/kpat/freecell-solver/ms_ca.h b/kpat/freecell-solver/ms_ca.h
new file mode 100644
index 00000000..5c1b44ec
--- /dev/null
+++ b/kpat/freecell-solver/ms_ca.h
@@ -0,0 +1,33 @@
+/*
+ * ms_ca.h - A header file for a (possibly inline) function that compactly
+ * allocates a move stack.
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2002
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ * */
+
+#include "inline.h"
+
+static GCC_INLINE fcs_move_stack_t * freecell_solver_move_stack_compact_allocate(freecell_solver_hard_thread_t * hard_thread, fcs_move_stack_t * old_move_stack_to_parent)
+{
+ char * ptr;
+ fcs_move_stack_t * new_move_stack_to_parent;
+ fcs_move_t * new_moves_to_parent;
+
+ fcs_compact_alloc_typed_ptr_into_var(
+ ptr,
+ char,
+ hard_thread->move_stacks_allocator,
+ (sizeof(fcs_move_stack_t) + sizeof(fcs_move_t)*old_move_stack_to_parent->num_moves)
+ );
+ new_move_stack_to_parent = (fcs_move_stack_t *)ptr;
+ new_moves_to_parent = (fcs_move_t *)(ptr+sizeof(fcs_move_stack_t));
+ new_move_stack_to_parent->moves = new_moves_to_parent;
+ new_move_stack_to_parent->num_moves =
+ new_move_stack_to_parent->max_num_moves =
+ old_move_stack_to_parent->num_moves;
+ memcpy(new_moves_to_parent, old_move_stack_to_parent->moves, sizeof(fcs_move_t)*old_move_stack_to_parent->num_moves);
+ return new_move_stack_to_parent;
+}
+
diff --git a/kpat/freecell-solver/pqueue.c b/kpat/freecell-solver/pqueue.c
new file mode 100644
index 00000000..086cce96
--- /dev/null
+++ b/kpat/freecell-solver/pqueue.c
@@ -0,0 +1,173 @@
+/*
+ pqueue.c - implementation of a priority queue by using a binary heap.
+
+ Originally written by Justin-Heyes Jones
+ Modified by Shlomi Fish, 2000
+
+ This file is in the public domain (it's uncopyrighted).
+
+ Check out Justin-Heyes Jones' A* page from which this code has
+ originated:
+ http://www.geocities.com/jheyesjones/astar.html
+ */
+
+/* manage a priority queue as a heap
+ the heap is implemented as a fixed size array of pointers to your data */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "jhjtypes.h"
+
+#include "pqueue.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+#define TRUE 1
+#define FALSE 0
+
+/* initialise the priority queue with a maximum size of maxelements. maxrating is the highest or lowest value of an
+ entry in the pqueue depending on whether it is ascending or descending respectively. Finally the bool32 tells you whether
+ the list is sorted ascending or descending... */
+
+void freecell_solver_PQueueInitialise(
+ PQUEUE *pq,
+ int32 MaxElements
+ )
+{
+ pq->MaxSize = MaxElements;
+
+ pq->CurrentSize = 0;
+
+ pq->Elements = (pq_element_t*) malloc( sizeof( pq_element_t ) * (MaxElements + 1) );
+
+ if( pq->Elements == NULL )
+ {
+ printf( "Memory alloc failed!\n" );
+ }
+}
+
+/* join a priority queue
+ returns TRUE if successful, FALSE if fails. (You fail by filling the pqueue.)
+ PGetRating is a function which returns the rating of the item you're adding for sorting purposes */
+
+int freecell_solver_PQueuePush( PQUEUE *pq, void *item, pq_rating_t r)
+{
+ uint32 i;
+ pq_element_t * Elements = pq->Elements;
+
+ int32 CurrentSize = pq->CurrentSize;
+
+ if (CurrentSize == pq->MaxSize )
+ {
+ int new_size;
+ new_size = pq->MaxSize + 256;
+ pq->Elements = Elements = (pq_element_t *)realloc( Elements, sizeof(pq_element_t) * (new_size+1));
+ pq->MaxSize = new_size;
+ }
+
+ {
+ /* set i to the first unused element and increment CurrentSize */
+
+ i = (++CurrentSize);
+
+ /* while the parent of the space we're putting the new node into is worse than
+ our new node, swap the space with the worse node. We keep doing that until we
+ get to a worse node or until we get to the top
+
+ note that we also can sort so that the minimum elements bubble up so we need to loops
+ with the comparison operator flipped... */
+
+ {
+
+ while( ( i==PQ_FIRST_ENTRY ?
+ (PQUEUE_MaxRating) /* return biggest possible rating if first element */
+ :
+ (PGetRating(Elements[ PQ_PARENT_INDEX(i) ]) )
+ )
+ < r
+ )
+ {
+ Elements[ i ] = Elements[ PQ_PARENT_INDEX(i) ];
+
+ i = PQ_PARENT_INDEX(i);
+ }
+ }
+
+ /* then add the element at the space we created. */
+ Elements[i].item = item;
+ Elements[i].rating = r;
+ }
+
+ pq->CurrentSize = CurrentSize;
+
+ return TRUE;
+
+}
+
+#define PQueueIsEmpty(pq) ((pq)->CurrentSize == 0)
+
+/* free up memory for pqueue */
+void freecell_solver_PQueueFree( PQUEUE *pq )
+{
+ free( pq->Elements );
+}
+
+/* remove the first node from the pqueue and provide a pointer to it */
+
+void *freecell_solver_PQueuePop( PQUEUE *pq)
+{
+ int32 i;
+ int32 child;
+ pq_element_t * Elements = pq->Elements;
+ int32 CurrentSize = pq->CurrentSize;
+
+ pq_element_t pMaxElement;
+ pq_element_t pLastElement;
+
+ if( PQueueIsEmpty( pq ) )
+ {
+ return NULL;
+ }
+
+ pMaxElement = Elements[PQ_FIRST_ENTRY];
+
+ /* get pointer to last element in tree */
+ pLastElement = Elements[ CurrentSize-- ];
+
+ {
+
+ /* code to pop an element from an ascending (top to bottom) pqueue */
+
+ /* UNTESTED */
+
+ for( i=PQ_FIRST_ENTRY; (child = PQ_LEFT_CHILD_INDEX(i)) <= CurrentSize; i=child )
+ {
+ /* set child to the smaller of the two children... */
+
+ if( (child != CurrentSize) &&
+ (PGetRating(Elements[child + 1]) > PGetRating(Elements[child])) )
+ {
+ child ++;
+ }
+
+ if( PGetRating( pLastElement ) < PGetRating( Elements[ child ] ) )
+ {
+ Elements[ i ] = Elements[ child ];
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ Elements[i] = pLastElement;
+ pq->CurrentSize = CurrentSize;
+
+ return pMaxElement.item;
+}
+
+
diff --git a/kpat/freecell-solver/pqueue.h b/kpat/freecell-solver/pqueue.h
new file mode 100644
index 00000000..cf5f5372
--- /dev/null
+++ b/kpat/freecell-solver/pqueue.h
@@ -0,0 +1,71 @@
+/*
+ pqueue.h - header file for the priority queue implementation.
+
+ Originally written by Justin-Heyes Jones
+ Modified by Shlomi Fish, 2000
+
+ This file is in the public domain (it's uncopyrighted).
+
+ Check out Justin-Heyes Jones' A* page from which this code has
+ originated:
+ http://www.geocities.com/jheyesjones/astar.html
+*/
+
+#ifndef FC_SOLVE__PQUEUE_H
+#define FC_SOLVE__PQUEUE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <limits.h>
+
+#include "jhjtypes.h"
+
+#define PQUEUE_MaxRating INT_MAX
+
+typedef int32 pq_rating_t;
+
+typedef struct struct_pq_element_t
+{
+ void * item;
+ pq_rating_t rating;
+} pq_element_t;
+
+typedef struct _PQUEUE
+{
+ int32 MaxSize;
+ int32 CurrentSize;
+ pq_element_t * Elements; /* pointer to void pointers */
+ pq_rating_t MaxRating; /* biggest element possible */
+} PQUEUE;
+
+/* given an index to any element in a binary tree stored in a linear array with the root at 1 and
+ a "sentinel" value at 0 these macros are useful in making the code clearer */
+
+/* the parent is always given by index/2 */
+#define PQ_PARENT_INDEX(i) ((i)>>1)
+#define PQ_FIRST_ENTRY (1)
+
+/* left and right children are index * 2 and (index * 2) +1 respectively */
+#define PQ_LEFT_CHILD_INDEX(i) ((i)<<1)
+#define PQ_RIGHT_CHILD_INDEX(i) (((i)<<1)+1)
+
+void freecell_solver_PQueueInitialise(
+ PQUEUE *pq,
+ int32 MaxElements
+ );
+
+void freecell_solver_PQueueFree( PQUEUE *pq );
+
+int freecell_solver_PQueuePush( PQUEUE *pq, void *item, pq_rating_t);
+
+void *freecell_solver_PQueuePop( PQUEUE *pq);
+
+#define PGetRating(elem) ((elem).rating)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifdef FC_SOLVE__PQUEUE_H */
diff --git a/kpat/freecell-solver/prefix.h b/kpat/freecell-solver/prefix.h
new file mode 100644
index 00000000..b3b5094e
--- /dev/null
+++ b/kpat/freecell-solver/prefix.h
@@ -0,0 +1,4 @@
+#define FREECELL_SOLVER_PREFIX "/usr/local"
+
+#define FREECELL_SOLVER_PKG_DATA_DIR "/usr/local/share/freecell-solver/"
+
diff --git a/kpat/freecell-solver/preset.c b/kpat/freecell-solver/preset.c
new file mode 100644
index 00000000..16a02f1d
--- /dev/null
+++ b/kpat/freecell-solver/preset.c
@@ -0,0 +1,637 @@
+/*
+ * preset.c - game presets management for Freecell Solver
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ *
+ */
+
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "fcs.h"
+#include "preset.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+enum fcs_presets_ids
+{
+ FCS_PRESET_BAKERS_DOZEN,
+ FCS_PRESET_BAKERS_GAME,
+ FCS_PRESET_CRUEL,
+ FCS_PRESET_DER_KATZENSCHWANZ,
+ FCS_PRESET_DIE_SCHLANGE,
+ FCS_PRESET_EIGHT_OFF,
+ FCS_PRESET_FAN,
+ FCS_PRESET_FORECELL,
+ FCS_PRESET_FREECELL,
+ FCS_PRESET_GOOD_MEASURE,
+ FCS_PRESET_KINGS_ONLY_BAKERS_GAME,
+ FCS_PRESET_RELAXED_FREECELL,
+ FCS_PRESET_RELAXED_SEAHAVEN_TOWERS,
+ FCS_PRESET_SEAHAVEN_TOWERS,
+ FCS_PRESET_SIMPLE_SIMON,
+ FCS_PRESET_YUKON,
+ FCS_PRESET_BELEAGUERED_CASTLE
+};
+
+static const fcs_preset_t fcs_presets[16] =
+{
+ {
+ FCS_PRESET_BAKERS_DOZEN,
+ 0,
+ 13,
+ 1,
+
+ FCS_SEQ_BUILT_BY_RANK,
+ 0,
+ FCS_ES_FILLED_BY_NONE,
+
+ "0123456789",
+ "0123456789",
+ },
+ {
+ FCS_PRESET_BAKERS_GAME,
+ 4,
+ 8,
+ 1,
+
+ FCS_SEQ_BUILT_BY_SUIT,
+ 0,
+ FCS_ES_FILLED_BY_ANY_CARD,
+
+ "[01][23456789]",
+ "0123456789",
+ },
+ {
+ FCS_PRESET_BELEAGUERED_CASTLE,
+ 0,
+ 8,
+ 1,
+
+ FCS_SEQ_BUILT_BY_RANK,
+ 0,
+ FCS_ES_FILLED_BY_ANY_CARD,
+
+ "[01][23456789]",
+ "0123456789",
+ },
+ {
+ FCS_PRESET_CRUEL,
+ 0,
+ 12,
+ 1,
+
+ FCS_SEQ_BUILT_BY_SUIT,
+ 0,
+ FCS_ES_FILLED_BY_NONE,
+
+ "0123456789",
+ "0123456789",
+ },
+ {
+ FCS_PRESET_DER_KATZENSCHWANZ,
+ 8,
+ 9,
+ 2,
+
+ FCS_SEQ_BUILT_BY_ALTERNATE_COLOR,
+ 1,
+ FCS_ES_FILLED_BY_NONE,
+
+ "[01][23456789]",
+ "0123456789",
+ },
+ {
+ FCS_PRESET_DIE_SCHLANGE,
+ 8,
+ 9,
+ 2,
+
+ FCS_SEQ_BUILT_BY_ALTERNATE_COLOR,
+ 0,
+ FCS_ES_FILLED_BY_NONE,
+
+ "[01][23456789]",
+ "0123456789",
+ },
+ {
+ FCS_PRESET_EIGHT_OFF,
+ 8,
+ 8,
+ 1,
+
+ FCS_SEQ_BUILT_BY_SUIT,
+ 0,
+ FCS_ES_FILLED_BY_KINGS_ONLY,
+
+ "[01][23456789]",
+ "0123456789",
+ },
+ {
+ FCS_PRESET_FAN,
+ 0,
+ 18,
+ 1,
+
+ FCS_SEQ_BUILT_BY_SUIT,
+ 0,
+ FCS_ES_FILLED_BY_KINGS_ONLY,
+
+ "[01][23456789]",
+ "0123456789",
+ },
+ {
+ FCS_PRESET_FORECELL,
+ 4,
+ 8,
+ 1,
+
+ FCS_SEQ_BUILT_BY_ALTERNATE_COLOR,
+ 0,
+ FCS_ES_FILLED_BY_KINGS_ONLY,
+
+ "[01][23456789]",
+ "0123456789",
+ },
+ {
+ FCS_PRESET_FREECELL,
+ 4,
+ 8,
+ 1,
+
+ FCS_SEQ_BUILT_BY_ALTERNATE_COLOR,
+ 0,
+ FCS_ES_FILLED_BY_ANY_CARD,
+
+ "[01][23456789]",
+ "0123456789",
+ },
+ {
+ FCS_PRESET_GOOD_MEASURE,
+ 0,
+ 10,
+ 1,
+
+ FCS_SEQ_BUILT_BY_RANK,
+ 0,
+ FCS_ES_FILLED_BY_NONE,
+
+ "0123456789",
+ "0123456789",
+ },
+ {
+ FCS_PRESET_KINGS_ONLY_BAKERS_GAME,
+ 4,
+ 8,
+ 1,
+
+ FCS_SEQ_BUILT_BY_SUIT,
+ 0,
+ FCS_ES_FILLED_BY_KINGS_ONLY,
+
+ "[01][23456789]",
+ "0123456789",
+ },
+ {
+ FCS_PRESET_RELAXED_FREECELL,
+ 4,
+ 8,
+ 1,
+
+ FCS_SEQ_BUILT_BY_ALTERNATE_COLOR,
+ 1,
+ FCS_ES_FILLED_BY_ANY_CARD,
+
+ "[01][23456789]",
+ "0123456789",
+ },
+ {
+ FCS_PRESET_RELAXED_SEAHAVEN_TOWERS,
+ 4,
+ 10,
+ 1,
+
+ FCS_SEQ_BUILT_BY_SUIT,
+ 1,
+ FCS_ES_FILLED_BY_KINGS_ONLY,
+
+ "[01][23456789]",
+ "0123456789",
+ },
+ {
+ FCS_PRESET_SEAHAVEN_TOWERS,
+ 4,
+ 10,
+ 1,
+
+ FCS_SEQ_BUILT_BY_SUIT,
+ 0,
+ FCS_ES_FILLED_BY_KINGS_ONLY,
+
+ "[01][23456789]",
+ "0123456789",
+ },
+ {
+ FCS_PRESET_SIMPLE_SIMON,
+ 0,
+ 10,
+ 1,
+
+ FCS_SEQ_BUILT_BY_SUIT,
+ 0,
+ FCS_ES_FILLED_BY_ANY_CARD,
+
+ "abcdefgh",
+ "abcdefgh",
+ },
+};
+
+struct fcs_preset_name_struct
+{
+ const char name[32];
+ int preset_id;
+};
+
+typedef struct fcs_preset_name_struct fcs_preset_name_t;
+
+static const fcs_preset_name_t fcs_preset_names[23] =
+{
+ {
+ "bakers_dozen",
+ FCS_PRESET_BAKERS_DOZEN,
+ },
+ {
+ "bakers_game",
+ FCS_PRESET_BAKERS_GAME,
+ },
+ {
+ "beleaguered_castle",
+ FCS_PRESET_BELEAGUERED_CASTLE,
+ },
+ {
+ "citadel",
+ FCS_PRESET_BELEAGUERED_CASTLE,
+ },
+ {
+ "cruel",
+ FCS_PRESET_CRUEL,
+ },
+ {
+ "der_katzenschwanz",
+ FCS_PRESET_DER_KATZENSCHWANZ,
+ },
+ {
+ "der_katz",
+ FCS_PRESET_DER_KATZENSCHWANZ,
+ },
+ {
+ "die_schlange",
+ FCS_PRESET_DIE_SCHLANGE,
+ },
+ {
+ "eight_off",
+ FCS_PRESET_EIGHT_OFF,
+ },
+ {
+ "fan",
+ FCS_PRESET_FAN,
+ },
+ {
+ "forecell",
+ FCS_PRESET_FORECELL,
+ },
+ {
+ "freecell",
+ FCS_PRESET_FREECELL,
+ },
+ {
+ "good_measure",
+ FCS_PRESET_GOOD_MEASURE,
+ },
+ {
+ "ko_bakers_game",
+ FCS_PRESET_KINGS_ONLY_BAKERS_GAME,
+ },
+ {
+ "kings_only_bakers_game",
+ FCS_PRESET_KINGS_ONLY_BAKERS_GAME,
+ },
+ {
+ "relaxed_freecell",
+ FCS_PRESET_RELAXED_FREECELL,
+ },
+ {
+ "relaxed_seahaven_towers",
+ FCS_PRESET_RELAXED_SEAHAVEN_TOWERS,
+ },
+ {
+ "relaxed_seahaven",
+ FCS_PRESET_RELAXED_SEAHAVEN_TOWERS,
+ },
+ {
+ "seahaven_towers",
+ FCS_PRESET_SEAHAVEN_TOWERS,
+ },
+ {
+ "seahaven",
+ FCS_PRESET_SEAHAVEN_TOWERS,
+ },
+ {
+ "simple_simon",
+ FCS_PRESET_SIMPLE_SIMON,
+ },
+ {
+ "streets_and_alleys",
+ FCS_PRESET_BELEAGUERED_CASTLE,
+ },
+ {
+ "yukon",
+ FCS_PRESET_YUKON,
+ },
+};
+
+static int fcs_get_preset_id_by_name(
+ const char * name
+)
+{
+ int a;
+ int ret = -1;
+ int num_elems;
+
+ num_elems = ( (int) (sizeof(fcs_preset_names)/sizeof(fcs_preset_names[0])));
+ for(a=0;a<num_elems;a++)
+ {
+ if (!strcmp(name, fcs_preset_names[a].name))
+ {
+ ret = fcs_preset_names[a].preset_id;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int freecell_solver_char_to_test_num(char c)
+{
+ if ((c >= '0') && (c <= '9'))
+ {
+ return c-'0';
+ }
+ else if ((c >= 'a') && (c <= 'h'))
+ {
+ return c-'a'+10;
+ }
+ else if ((c >= 'A') && (c <= 'Z'))
+ {
+ return c-'A'+18;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+#ifndef min
+#define min(a,b) (((a)<(b))?(a):(b))
+#endif
+
+struct internal_tests_order_struct
+{
+ int tests_order_num;
+ int tests_order[FCS_TESTS_NUM];
+};
+
+typedef struct internal_tests_order_struct internal_tests_order_t;
+
+int freecell_solver_apply_tests_order(
+ fcs_tests_order_t * tests_order,
+ const char * string,
+ char * * error_string
+ )
+
+{
+ int a;
+ int len;
+ int test_index;
+ int is_group, is_start_group;
+ if (tests_order->tests)
+ {
+ free(tests_order->tests);
+ tests_order->max_num = 10;
+ tests_order->num = 0;
+ tests_order->tests = malloc(sizeof(tests_order->tests[0])*tests_order->max_num );
+ }
+
+#if 0
+ instance->tests_order_num = min(strlen(string), FCS_TESTS_NUM);
+#endif
+ len = strlen(string);
+ test_index = 0;
+ is_group = 0;
+ is_start_group = 0;
+ for(a=0;(a<len) ;a++)
+ {
+ if ((string[a] == '(') || (string[a] == '['))
+ {
+ if (is_group)
+ {
+ *error_string = strdup("There's a nested random group.");
+ return 1;
+ }
+ is_group = 1;
+ is_start_group = 1;
+ continue;
+ }
+
+ if ((string[a] == ')') || (string[a] == ']'))
+ {
+ if (is_start_group)
+ {
+ *error_string = strdup("There's an empty group.");
+ return 2;
+ }
+ if (! is_group)
+ {
+ *error_string = strdup("There's a renegade right parenthesis or bracket.");
+ return 3;
+ }
+ is_group = 0;
+ is_start_group = 0;
+ continue;
+ }
+ if (test_index == tests_order->max_num)
+ {
+ tests_order->max_num += 10;
+ tests_order->tests = realloc(tests_order->tests, sizeof(tests_order->tests[0]) * tests_order->max_num);
+ }
+ tests_order->tests[test_index] = (freecell_solver_char_to_test_num(string[a])%FCS_TESTS_NUM) | (is_group ? FCS_TEST_ORDER_FLAG_RANDOM : 0) | (is_start_group ? FCS_TEST_ORDER_FLAG_START_RANDOM_GROUP : 0);
+
+ test_index++;
+ is_start_group = 0;
+ }
+ if (a != len)
+ {
+ *error_string = strdup("The Input string is too long.");
+ return 4;
+ }
+
+ tests_order->num = test_index;
+ *error_string = NULL;
+
+ return 0;
+}
+
+int freecell_solver_apply_preset_by_ptr(
+ freecell_solver_instance_t * instance,
+ const fcs_preset_t * preset_ptr
+ )
+{
+ char * no_use;
+
+#define preset (*preset_ptr)
+ if (preset.freecells_num > MAX_NUM_FREECELLS)
+ {
+ return FCS_PRESET_CODE_FREECELLS_EXCEED_MAX;
+ }
+ if (preset.stacks_num > MAX_NUM_STACKS)
+ {
+ return FCS_PRESET_CODE_STACKS_EXCEED_MAX;
+ }
+ if (preset.decks_num > MAX_NUM_DECKS)
+ {
+ return FCS_PRESET_CODE_DECKS_EXCEED_MAX;
+ }
+ instance->freecells_num = preset.freecells_num;
+ instance->stacks_num = preset.stacks_num;
+ instance->decks_num = preset.decks_num;
+
+ instance->sequences_are_built_by = preset.sequences_are_built_by;
+ instance->unlimited_sequence_move = preset.unlimited_sequence_move;
+ instance->empty_stacks_fill = preset.empty_stacks_fill;
+
+ /*
+ * This code makes sure that all the tests in all the existing
+ * soft threads are acceptable by the new preset.
+ * */
+
+ {
+ int ht_idx, st_idx;
+ for(ht_idx = 0; ht_idx < instance->num_hard_threads ; ht_idx++)
+ {
+ for(st_idx = 0; st_idx < instance->hard_threads[ht_idx]->num_soft_threads; st_idx++)
+ {
+ freecell_solver_soft_thread_t * soft_thread = instance->hard_threads[ht_idx]->soft_threads[st_idx];
+
+ int num_valid_tests;
+ const char * s;
+
+ /* Check every test */
+
+ for(num_valid_tests=0;num_valid_tests < soft_thread->tests_order.num; num_valid_tests++)
+ {
+ for(s = preset.allowed_tests;*s != '\0';s++)
+ {
+ /* Check if this test corresponds to this character */
+ if ((soft_thread->tests_order.tests[num_valid_tests] & FCS_TEST_ORDER_NO_FLAGS_MASK) == ((freecell_solver_char_to_test_num(*s)%FCS_TESTS_NUM)))
+ {
+ break;
+ }
+ }
+ /* If the end of the string was reached, it means
+ * this test is unacceptable by this preset. */
+ if (*s == '\0')
+ {
+ break;
+ }
+ }
+ if (num_valid_tests < soft_thread->tests_order.num)
+ {
+ freecell_solver_apply_tests_order(
+ &(soft_thread->tests_order),
+ preset.tests_order,
+ &no_use);
+ }
+ }
+ }
+ }
+
+ /* Assign the master tests order */
+
+ {
+ freecell_solver_apply_tests_order(
+ &(instance->instance_tests_order),
+ preset.tests_order,
+ &no_use);
+ }
+#undef preset
+ return FCS_PRESET_CODE_OK;
+}
+
+static int fcs_get_preset_by_id(
+ int preset_id,
+ const fcs_preset_t * * preset_ptr
+ )
+{
+ int preset_index;
+ int num_elems;
+
+ num_elems = ( (int) (sizeof(fcs_presets)/sizeof(fcs_presets[0])));
+
+ for(preset_index=0 ; preset_index < num_elems ; preset_index++)
+ {
+ if (fcs_presets[preset_index].preset_id == preset_id)
+ {
+ *preset_ptr = &(fcs_presets[preset_index]);
+ return FCS_PRESET_CODE_OK;
+ }
+ }
+
+ return FCS_PRESET_CODE_NOT_FOUND;
+}
+
+int freecell_solver_get_preset_by_name(
+ const char * name,
+ const fcs_preset_t * * preset_ptr
+ )
+{
+ int preset_id;
+
+ preset_id = fcs_get_preset_id_by_name(name);
+ if (preset_id >= 0)
+ {
+ return fcs_get_preset_by_id(
+ preset_id,
+ preset_ptr
+ );
+ }
+ else
+ {
+ return FCS_PRESET_CODE_NOT_FOUND;
+ }
+}
+
+int freecell_solver_apply_preset_by_name(
+ freecell_solver_instance_t * instance,
+ const char * name
+ )
+{
+ int ret;
+ const fcs_preset_t * preset_ptr;
+
+ ret = freecell_solver_get_preset_by_name(
+ name,
+ &preset_ptr
+ );
+
+ if (ret != FCS_PRESET_CODE_OK)
+ {
+ return ret;
+ }
+
+ return freecell_solver_apply_preset_by_ptr(instance, preset_ptr);
+}
diff --git a/kpat/freecell-solver/preset.h b/kpat/freecell-solver/preset.h
new file mode 100644
index 00000000..553e9d07
--- /dev/null
+++ b/kpat/freecell-solver/preset.h
@@ -0,0 +1,62 @@
+/*
+ * fcs.h - header file of the preset management functions for Freecell Solver.
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+
+#ifndef FC_SOLVE__PRESET_H
+#define FC_SOLVE__PRESET_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "fcs.h"
+
+struct fcs_preset_struct
+{
+ int preset_id;
+ int freecells_num;
+ int stacks_num;
+ int decks_num;
+
+ int sequences_are_built_by;
+ int unlimited_sequence_move;
+ int empty_stacks_fill;
+
+ char tests_order[FCS_TESTS_NUM*3+1];
+ char allowed_tests[FCS_TESTS_NUM*3+1];
+};
+
+typedef struct fcs_preset_struct fcs_preset_t;
+
+extern int freecell_solver_apply_preset_by_ptr(
+ freecell_solver_instance_t * instance,
+ const fcs_preset_t * preset_ptr
+ );
+
+extern int freecell_solver_apply_preset_by_name(
+ freecell_solver_instance_t * instance,
+ const char * name
+ );
+
+extern int freecell_solver_apply_tests_order(
+ fcs_tests_order_t * tests_order,
+ const char * string,
+ char * * error_string
+ );
+
+extern int freecell_solver_get_preset_by_name(
+ const char * name,
+ const fcs_preset_t * * preset_ptr
+ );
+
+#define fcs_duplicate_preset(d,s) ((d) = (s))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/kpat/freecell-solver/rand.c b/kpat/freecell-solver/rand.c
new file mode 100644
index 00000000..d5151e8e
--- /dev/null
+++ b/kpat/freecell-solver/rand.c
@@ -0,0 +1,30 @@
+#include <stdlib.h>
+
+#ifdef DMALLOC
+#include <dmalloc.h>
+#endif
+
+#include "rand.h"
+
+fcs_rand_t * freecell_solver_rand_alloc(unsigned int seed)
+{
+ fcs_rand_t * ret;
+
+ ret = malloc(sizeof(fcs_rand_t));
+ ret->seed = (long)seed;
+
+ return ret;
+}
+
+void freecell_solver_rand_free(fcs_rand_t * rand)
+{
+ free(rand);
+}
+
+
+void freecell_solver_rand_srand(fcs_rand_t * rand, unsigned int seed)
+{
+ rand->seed = seed;
+}
+
+
diff --git a/kpat/freecell-solver/rand.h b/kpat/freecell-solver/rand.h
new file mode 100644
index 00000000..0cecfafd
--- /dev/null
+++ b/kpat/freecell-solver/rand.h
@@ -0,0 +1,49 @@
+
+#ifndef FC_SOLVE__RAND_H
+#define FC_SOLVE__RAND_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "inline.h"
+
+struct fcs_rand_struct
+{
+ unsigned long seed;
+};
+
+typedef struct fcs_rand_struct fcs_rand_t;
+
+extern fcs_rand_t * freecell_solver_rand_alloc(unsigned int seed);
+extern void freecell_solver_rand_free(fcs_rand_t * rand);
+
+extern void freecell_solver_rand_srand(fcs_rand_t * rand, unsigned int seed);
+
+static GCC_INLINE int freecell_solver_rand_rand15(fcs_rand_t * rand)
+{
+ rand->seed = (rand->seed * 214013 + 2531011);
+ return (rand->seed >> 16) & 0x7fff;
+}
+
+/*
+ *
+ * This function constructs a larger integral number of out of two
+ * 15-bit ones.
+ *
+ * */
+static GCC_INLINE int freecell_solver_rand_get_random_number(fcs_rand_t * rand)
+{
+ int one, two;
+ one = freecell_solver_rand_rand15(rand);
+ two = freecell_solver_rand_rand15(rand);
+
+ return (one | (two << 15));
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/kpat/freecell-solver/scans.c b/kpat/freecell-solver/scans.c
new file mode 100644
index 00000000..5c579739
--- /dev/null
+++ b/kpat/freecell-solver/scans.c
@@ -0,0 +1,1170 @@
+/*
+ * scans.c - The code that relates to the various scans.
+ * Currently Hard DFS, Soft-DFS, Random-DFS, A* and BFS are implemented.
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000-2001
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "fcs_config.h"
+
+/* So FCS_STATE_STORAGE and friends would be defined */
+#if FCS_STATE_STORAGE==FCS_STATE_STORAGE_LIBREDBLACK_TREE
+#include <search.h>
+#endif
+
+#include "state.h"
+#include "card.h"
+#include "fcs_dm.h"
+#include "fcs.h"
+
+#include "fcs_isa.h"
+
+#include "test_arr.h"
+#include "caas.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+static pq_rating_t freecell_solver_a_star_rate_state(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations);
+
+#define freecell_solver_a_star_enqueue_state(soft_thread,ptr_state_with_locations) \
+ { \
+ freecell_solver_PQueuePush( \
+ a_star_pqueue, \
+ ptr_state_with_locations, \
+ freecell_solver_a_star_rate_state(soft_thread, ptr_state_with_locations) \
+ ); \
+ }
+
+
+#define freecell_solver_bfs_enqueue_state(soft_thread, state) \
+ { \
+ fcs_states_linked_list_item_t * last_item_next; \
+ last_item_next = bfs_queue_last_item->next = (fcs_states_linked_list_item_t*)malloc(sizeof(fcs_states_linked_list_item_t)); \
+ bfs_queue_last_item->s = state; \
+ last_item_next->next = NULL; \
+ bfs_queue_last_item = last_item_next; \
+ }
+
+#define the_state (ptr_state_with_locations->s)
+
+int freecell_solver_hard_dfs_solve_for_state(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int depth,
+ int ignore_osins
+ )
+
+{
+ freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread;
+ freecell_solver_instance_t * instance = hard_thread->instance;
+
+ int a;
+ int check;
+
+ int num_freestacks, num_freecells;
+
+ int iter_num = instance->num_times;
+
+ fcs_derived_states_list_t derived;
+
+ int derived_state_index;
+
+ int ret_value;
+
+ int freecells_num, stacks_num;
+
+ int calc_real_depth, scans_synergy;
+
+ freecells_num = instance->freecells_num;
+ stacks_num = instance->stacks_num;
+
+ derived.num_states = derived.max_num_states = 0;
+ derived.states = NULL;
+
+ calc_real_depth = instance->calc_real_depth;
+ scans_synergy = instance->scans_synergy;
+
+ /*
+ * If this state has not been visited before - increase the number of
+ * iterations this program has seen, and output this state again.
+ *
+ * I'm doing this in order to make the output of a stopped and
+ * resumed run consistent with the output of a normal (all-in-one-time)
+ * run.
+ * */
+ if (!is_scan_visited(ptr_state_with_locations, soft_thread->id))
+ {
+ if (instance->debug_iter_output)
+ {
+ instance->debug_iter_output_func(
+ (void*)instance->debug_iter_output_context,
+ iter_num,
+ depth,
+ (void*)instance,
+ ptr_state_with_locations,
+ 0 /* It's a temporary kludge */
+ );
+ }
+ /* Increase the number of iterations */
+ instance->num_times++;
+ hard_thread->num_times++;
+ ptr_state_with_locations->visited_iter = iter_num;
+ }
+
+ /* Mark this state as visited, so it won't be recursed into again. */
+ set_scan_visited(ptr_state_with_locations, soft_thread->id);
+
+ /* Count the free-cells */
+ num_freecells = 0;
+ for(a=0;a<freecells_num;a++)
+ {
+ if (fcs_freecell_card_num(the_state, a) == 0)
+ {
+ num_freecells++;
+ }
+ }
+
+ /* Count the number of unoccupied stacks */
+
+ num_freestacks = 0;
+ for(a=0;a<stacks_num;a++)
+ {
+ if (fcs_stack_len(the_state, a) == 0)
+ {
+ num_freestacks++;
+ }
+ }
+
+
+ /* Let's check if this state is finished, and if so return 0; */
+ if ((num_freestacks == stacks_num) && (num_freecells == freecells_num))
+ {
+ instance->final_state = ptr_state_with_locations;
+
+ ret_value = FCS_STATE_WAS_SOLVED;
+ goto free_derived;
+ }
+
+ calculate_real_depth(ptr_state_with_locations);
+
+ for(a=0 ;
+ a < soft_thread->tests_order.num;
+ a++)
+ {
+ derived.num_states = 0;
+
+ check =
+ freecell_solver_sfs_tests[soft_thread->tests_order.tests[a] & FCS_TEST_ORDER_NO_FLAGS_MASK ] (
+ soft_thread,
+ ptr_state_with_locations,
+ num_freestacks,
+ num_freecells,
+ &derived,
+ 0
+ );
+
+ if ((check == FCS_STATE_BEGIN_SUSPEND_PROCESS) ||
+ (check == FCS_STATE_SUSPEND_PROCESS))
+ {
+ if (check == FCS_STATE_BEGIN_SUSPEND_PROCESS)
+ {
+ soft_thread->num_solution_states = depth+1;
+
+ soft_thread->soft_dfs_info = malloc(sizeof(soft_thread->soft_dfs_info[0]) * soft_thread->num_solution_states);
+ }
+
+ soft_thread->soft_dfs_info[depth].state = ptr_state_with_locations;
+
+ ret_value = FCS_STATE_SUSPEND_PROCESS;
+
+ goto free_derived;
+ }
+
+ for(derived_state_index=0;derived_state_index<derived.num_states;derived_state_index++)
+ {
+ if (
+ (! (derived.states[derived_state_index]->visited &
+ FCS_VISITED_DEAD_END)
+ ) &&
+ (! is_scan_visited(
+ derived.states[derived_state_index],
+ soft_thread->id)
+ )
+ )
+ {
+ check =
+ freecell_solver_hard_dfs_solve_for_state(
+ soft_thread,
+ derived.states[derived_state_index],
+ depth+1,
+ ignore_osins
+ );
+
+ if ((check == FCS_STATE_SUSPEND_PROCESS) ||
+ (check == FCS_STATE_BEGIN_SUSPEND_PROCESS))
+ {
+
+ soft_thread->soft_dfs_info[depth].state = ptr_state_with_locations;
+
+ ret_value = FCS_STATE_SUSPEND_PROCESS;
+
+ goto free_derived;
+ }
+
+ if (check == FCS_STATE_WAS_SOLVED)
+ {
+ ret_value = FCS_STATE_WAS_SOLVED;
+
+ goto free_derived;
+ }
+ }
+ }
+ }
+
+ if (check_if_limits_exceeded())
+ {
+ soft_thread->num_solution_states = depth+1;
+
+ soft_thread->soft_dfs_info = malloc(sizeof(soft_thread->soft_dfs_info[0]) * soft_thread->num_solution_states);
+
+
+ soft_thread->soft_dfs_info[depth].state = ptr_state_with_locations;
+
+ ret_value = FCS_STATE_SUSPEND_PROCESS;
+
+ goto free_derived;
+ }
+
+ ret_value = FCS_STATE_IS_NOT_SOLVEABLE;
+
+ if (soft_thread->is_a_complete_scan)
+ {
+ mark_as_dead_end(ptr_state_with_locations);
+ }
+
+
+free_derived:
+ if (derived.states != NULL)
+ {
+ free(derived.states);
+ }
+
+ return ret_value;
+}
+
+
+int freecell_solver_hard_dfs_resume_solution(
+ freecell_solver_soft_thread_t * soft_thread,
+ int depth
+ )
+{
+ fcs_state_with_locations_t * ptr_state_with_locations;
+ int check;
+
+ ptr_state_with_locations = soft_thread->soft_dfs_info[depth].state;
+
+ if (depth < soft_thread->num_solution_states-1)
+ {
+ check = freecell_solver_hard_dfs_resume_solution(
+ soft_thread,
+ depth+1
+ );
+ }
+ else
+ {
+ free(soft_thread->soft_dfs_info);
+ soft_thread->soft_dfs_info = NULL;
+ check = FCS_STATE_IS_NOT_SOLVEABLE;
+ }
+
+ if (check == FCS_STATE_IS_NOT_SOLVEABLE)
+ {
+ check = freecell_solver_hard_dfs_solve_for_state(
+ soft_thread,
+ ptr_state_with_locations,
+ depth,
+ 1);
+ }
+ else if (check == FCS_STATE_WAS_SOLVED)
+ {
+ /* Do nothing - fall back to return check. */
+ }
+ else
+ {
+ if ((check == FCS_STATE_SUSPEND_PROCESS) || (check == FCS_STATE_WAS_SOLVED))
+ {
+
+ soft_thread->soft_dfs_info[depth].state = ptr_state_with_locations;
+ }
+ }
+
+ return check;
+}
+
+#undef state
+
+
+
+
+
+static void freecell_solver_increase_dfs_max_depth(
+ freecell_solver_soft_thread_t * soft_thread
+ )
+{
+ int new_dfs_max_depth = soft_thread->dfs_max_depth + 16;
+ int d;
+
+#define MYREALLOC(what) \
+ soft_thread->what = realloc( \
+ soft_thread->what, \
+ sizeof(soft_thread->what[0])*new_dfs_max_depth \
+ ); \
+
+ MYREALLOC(soft_dfs_info);
+#undef MYREALLOC
+
+ for(d=soft_thread->dfs_max_depth ; d<new_dfs_max_depth; d++)
+ {
+ soft_thread->soft_dfs_info[d].state = NULL;
+ soft_thread->soft_dfs_info[d].derived_states_list.max_num_states = 0;
+ soft_thread->soft_dfs_info[d].test_index = 0;
+ soft_thread->soft_dfs_info[d].current_state_index = 0;
+ soft_thread->soft_dfs_info[d].derived_states_list.num_states = 0;
+ soft_thread->soft_dfs_info[d].derived_states_list.states = NULL;
+ soft_thread->soft_dfs_info[d].derived_states_random_indexes = NULL;
+ soft_thread->soft_dfs_info[d].derived_states_random_indexes_max_size = 0;
+ }
+
+ soft_thread->dfs_max_depth = new_dfs_max_depth;
+}
+
+/*
+ freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume is the event loop of the
+ Random-DFS scan. DFS which is recursive in nature is handled here
+ without procedural recursion
+ by using some dedicated stacks for the traversal.
+ */
+#define the_state (ptr_state_with_locations->s)
+
+#define myreturn(ret_value) \
+ soft_thread->num_solution_states = depth+1; \
+ return (ret_value);
+
+int freecell_solver_soft_dfs_or_random_dfs_do_solve_or_resume(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations_orig,
+ int resume,
+ int to_randomize
+ )
+{
+ freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread;
+ freecell_solver_instance_t * instance = hard_thread->instance;
+
+ int depth;
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ * ptr_recurse_into_state_with_locations;
+ int a;
+ int check;
+ int do_first_iteration;
+ fcs_soft_dfs_stack_item_t * the_soft_dfs_info;
+ int freecells_num, stacks_num;
+ int dfs_max_depth;
+
+ int tests_order_num = soft_thread->tests_order.num;
+ int * tests_order_tests = soft_thread->tests_order.tests;
+ int calc_real_depth = instance->calc_real_depth;
+ int is_a_complete_scan = soft_thread->is_a_complete_scan;
+ int soft_thread_id = soft_thread->id;
+ int test_index, current_state_index;
+ fcs_derived_states_list_t * derived_states_list;
+ int to_reparent_states, scans_synergy;
+
+ freecells_num = instance->freecells_num;
+ stacks_num = instance->stacks_num;
+ to_reparent_states = instance->to_reparent_states;
+ scans_synergy = instance->scans_synergy;
+
+ if (!resume)
+ {
+ /*
+ Allocate some space for the states at depth 0.
+ */
+ depth=0;
+
+ freecell_solver_increase_dfs_max_depth(soft_thread);
+
+ /* Initialize the initial state to indicate it is the first */
+ ptr_state_with_locations_orig->parent = NULL;
+ ptr_state_with_locations_orig->moves_to_parent = NULL;
+ ptr_state_with_locations_orig->depth = 0;
+
+ soft_thread->soft_dfs_info[0].state = ptr_state_with_locations_orig;
+ }
+ else
+ {
+ /*
+ Set the initial depth to that of the last state encountered.
+ */
+ depth = soft_thread->num_solution_states - 1;
+ }
+
+ the_soft_dfs_info = &(soft_thread->soft_dfs_info[depth]);
+
+
+ dfs_max_depth = soft_thread->dfs_max_depth;
+ test_index = the_soft_dfs_info->test_index;
+ current_state_index = the_soft_dfs_info->current_state_index;
+ ptr_state_with_locations = the_soft_dfs_info->state;
+ derived_states_list = &(the_soft_dfs_info->derived_states_list);
+
+ calculate_real_depth(ptr_state_with_locations);
+
+ /*
+ The main loop.
+ */
+ while (depth >= 0)
+ {
+ /*
+ Increase the "maximal" depth if it about to be exceeded.
+ */
+ if (depth+1 >= dfs_max_depth)
+ {
+ freecell_solver_increase_dfs_max_depth(soft_thread);
+
+ /* Because the address of soft_thread->soft_dfs_info may
+ * be changed
+ * */
+ the_soft_dfs_info = &(soft_thread->soft_dfs_info[depth]);
+ dfs_max_depth = soft_thread->dfs_max_depth;
+ /* This too has to be re-synced */
+ derived_states_list = &(the_soft_dfs_info->derived_states_list);
+ }
+
+ /* All the resultant states in the last test conducted were covered */
+ if (current_state_index == derived_states_list->num_states)
+ {
+ if (test_index >= tests_order_num)
+ {
+ /* Backtrack to the previous depth. */
+
+ if (is_a_complete_scan)
+ {
+ ptr_state_with_locations->visited |= FCS_VISITED_ALL_TESTS_DONE;
+ mark_as_dead_end(ptr_state_with_locations);
+ }
+
+ depth--;
+
+ if (check_if_limits_exceeded())
+ {
+ the_soft_dfs_info->test_index = test_index;
+ the_soft_dfs_info->current_state_index = current_state_index;
+ myreturn(FCS_STATE_SUSPEND_PROCESS);
+ }
+
+ the_soft_dfs_info--;
+ /*
+ * depth (and evidently the_soft_dfs_info) might be invalid
+ * now, so we should check before we assign.
+ * */
+ if (depth >= 0)
+ {
+ test_index = the_soft_dfs_info->test_index;
+ current_state_index = the_soft_dfs_info->current_state_index;
+ derived_states_list = &(the_soft_dfs_info->derived_states_list);
+ ptr_state_with_locations = the_soft_dfs_info->state;
+ }
+ continue; /* Just to make sure depth is not -1 now */
+ }
+
+ derived_states_list->num_states = 0;
+
+ /* If this is the first test, then count the number of unoccupied
+ freeceels and stacks and check if we are done. */
+ if (test_index == 0)
+ {
+ int num_freestacks, num_freecells;
+
+ if (instance->debug_iter_output)
+ {
+#ifdef DEBUG
+ printf("ST Name: %s\n", soft_thread->name);
+#endif
+ instance->debug_iter_output_func(
+ (void*)instance->debug_iter_output_context,
+ instance->num_times,
+ depth,
+ (void*)instance,
+ ptr_state_with_locations,
+ ((depth == 0) ?
+ 0 :
+ soft_thread->soft_dfs_info[depth-1].state->visited_iter
+ )
+ );
+ }
+
+ /* Count the free-cells */
+ num_freecells = 0;
+ for(a=0;a<freecells_num;a++)
+ {
+ if (fcs_freecell_card_num(the_state, a) == 0)
+ {
+ num_freecells++;
+ }
+ }
+
+ /* Count the number of unoccupied stacks */
+
+ num_freestacks = 0;
+ for(a=0;a<stacks_num;a++)
+ {
+ if (fcs_stack_len(the_state, a) == 0)
+ {
+ num_freestacks++;
+ }
+ }
+
+ /* Check if we have reached the empty state */
+ if ((num_freestacks == stacks_num) && (num_freecells == freecells_num))
+ {
+ instance->final_state = ptr_state_with_locations;
+
+ myreturn(FCS_STATE_WAS_SOLVED);
+ }
+ /*
+ Cache num_freecells and num_freestacks in their
+ appropriate stacks, so they won't be calculated over and over
+ again.
+ */
+ the_soft_dfs_info->num_freecells = num_freecells;
+ the_soft_dfs_info->num_freestacks = num_freestacks;
+ }
+
+ /* Always do the first test */
+ do_first_iteration = 1;
+
+ while (
+ /* Make sure we do not exceed the number of tests */
+ (test_index < tests_order_num) &&
+ (
+ /* Always do the first test */
+ do_first_iteration ||
+ (
+ /* This is a randomized scan. Else - quit after the first iteration */
+ to_randomize &&
+ /* We are still on a random group */
+ (tests_order_tests[ test_index ] & FCS_TEST_ORDER_FLAG_RANDOM) &&
+ /* A new random group did not start */
+ (! (tests_order_tests[ test_index ] & FCS_TEST_ORDER_FLAG_START_RANDOM_GROUP))
+ )
+ )
+ )
+ {
+ do_first_iteration = 0;
+
+ check = freecell_solver_sfs_tests[tests_order_tests[
+ test_index
+ ] & FCS_TEST_ORDER_NO_FLAGS_MASK] (
+ soft_thread,
+ ptr_state_with_locations,
+ the_soft_dfs_info->num_freestacks,
+ the_soft_dfs_info->num_freecells,
+ derived_states_list,
+ to_reparent_states
+ );
+
+ if ((check == FCS_STATE_BEGIN_SUSPEND_PROCESS) ||
+ (check == FCS_STATE_EXCEEDS_MAX_NUM_TIMES) ||
+ (check == FCS_STATE_SUSPEND_PROCESS))
+ {
+ /* Have this test be re-performed */
+ derived_states_list->num_states = 0;
+ the_soft_dfs_info->current_state_index = 0;
+ the_soft_dfs_info->test_index = test_index;
+ myreturn(FCS_STATE_SUSPEND_PROCESS);
+ }
+
+ /* Move the counter to the next test */
+ test_index++;
+ }
+
+
+ {
+ int a, j;
+ int swap_save;
+ int * rand_array, * ra_ptr;
+ int num_states = derived_states_list->num_states;
+
+ if (num_states >
+ the_soft_dfs_info->derived_states_random_indexes_max_size)
+ {
+ the_soft_dfs_info->derived_states_random_indexes_max_size =
+ num_states;
+ the_soft_dfs_info->derived_states_random_indexes =
+ realloc(
+ the_soft_dfs_info->derived_states_random_indexes,
+ sizeof(the_soft_dfs_info->derived_states_random_indexes[0]) * the_soft_dfs_info->derived_states_random_indexes_max_size
+ );
+ }
+ rand_array = the_soft_dfs_info->derived_states_random_indexes;
+
+ for(a=0, ra_ptr = rand_array; a < num_states ; a++)
+ {
+ *(ra_ptr++) = a;
+ }
+ /* If we just conducted the tests for a random group -
+ * randomize. Else - keep those indexes as the unity vector.
+ *
+ * Also, do not randomize if this is a pure soft-DFS scan.
+ * */
+ if (to_randomize && tests_order_tests[ test_index-1 ] & FCS_TEST_ORDER_FLAG_RANDOM)
+ {
+ a = num_states-1;
+ while (a > 0)
+ {
+ j =
+ (
+ freecell_solver_rand_get_random_number(
+ soft_thread->rand_gen
+ )
+ % (a+1)
+ );
+
+ swap_save = rand_array[a];
+ rand_array[a] = rand_array[j];
+ rand_array[j] = swap_save;
+ a--;
+ }
+ }
+ }
+
+ /* We just performed a test, so the index of the first state that
+ ought to be checked in this depth is 0.
+ */
+ current_state_index = 0;
+ }
+
+ {
+ int num_states = derived_states_list->num_states;
+ fcs_state_with_locations_t * * derived_states = derived_states_list->states;
+ int * rand_array = the_soft_dfs_info->derived_states_random_indexes;
+
+ while (current_state_index <
+ num_states)
+ {
+ ptr_recurse_into_state_with_locations =
+ (derived_states[
+ rand_array[
+ current_state_index
+ ]
+ ]);
+
+ current_state_index++;
+ if (
+ (! (ptr_recurse_into_state_with_locations->visited &
+ FCS_VISITED_DEAD_END)
+ ) &&
+ (! is_scan_visited(
+ ptr_recurse_into_state_with_locations,
+ soft_thread_id)
+ )
+ )
+ {
+ instance->num_times++;
+ hard_thread->num_times++;
+
+ the_soft_dfs_info->test_index = test_index;
+ the_soft_dfs_info->current_state_index = current_state_index;
+
+ set_scan_visited(ptr_recurse_into_state_with_locations, soft_thread_id);
+
+ ptr_recurse_into_state_with_locations->visited_iter = instance->num_times;
+#if 0
+ ptr_recurse_into_state_with_locations->parent = ptr_state_with_locations;
+#endif
+
+ /*
+ I'm using current_state_indexes[depth]-1 because we already
+ increased it by one, so now it refers to the next state.
+ */
+ depth++;
+ the_soft_dfs_info++;
+ the_soft_dfs_info->state =
+ ptr_state_with_locations =
+ ptr_recurse_into_state_with_locations;
+ test_index = 0;
+ current_state_index = 0;
+ derived_states_list = &(the_soft_dfs_info->derived_states_list);
+ derived_states_list->num_states = 0;
+
+ calculate_real_depth(ptr_recurse_into_state_with_locations);
+
+ break;
+ }
+ }
+ }
+ }
+
+ soft_thread->num_solution_states = 0;
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+
+#undef state
+#undef myreturn
+
+#define FCS_A_STAR_CARDS_UNDER_SEQUENCES_EXPONENT 1.3
+#define FCS_A_STAR_SEQS_OVER_RENEGADE_CARDS_EXPONENT 1.3
+
+#define state (ptr_state_with_locations->s)
+
+void freecell_solver_a_star_initialize_rater(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations
+ )
+{
+ freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread;
+ freecell_solver_instance_t * instance = hard_thread->instance;
+
+ int a, c, cards_num;
+ fcs_card_t this_card, prev_card;
+ double cards_under_sequences;
+ int sequences_are_built_by = instance->sequences_are_built_by;
+
+
+ cards_under_sequences = 0;
+ for(a=0;a<instance->stacks_num;a++)
+ {
+ cards_num = fcs_stack_len(state, a);
+ if (cards_num <= 1)
+ {
+ continue;
+ }
+
+ c = cards_num-2;
+ this_card = fcs_stack_card(state, a, c+1);
+ prev_card = fcs_stack_card(state, a, c);
+ while (fcs_is_parent_card(this_card,prev_card) && (c >= 0))
+ {
+ c--;
+ this_card = prev_card;
+ if (c>=0)
+ {
+ prev_card = fcs_stack_card(state, a, c);
+ }
+ }
+ cards_under_sequences += pow(c+1, FCS_A_STAR_CARDS_UNDER_SEQUENCES_EXPONENT);
+ }
+ soft_thread->a_star_initial_cards_under_sequences = cards_under_sequences;
+}
+
+
+static pq_rating_t freecell_solver_a_star_rate_state(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations
+ )
+{
+ freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread;
+ freecell_solver_instance_t * instance = hard_thread->instance;
+
+ double ret=0;
+ int a, c, cards_num, num_cards_in_founds;
+ int num_freestacks, num_freecells;
+ fcs_card_t this_card, prev_card;
+ double cards_under_sequences, temp;
+ double seqs_over_renegade_cards;
+ int sequences_are_built_by = instance->sequences_are_built_by;
+ int freecells_num = instance->freecells_num;
+ int stacks_num = instance->stacks_num;
+ double * a_star_weights = soft_thread->a_star_weights;
+ int unlimited_sequence_move = instance->unlimited_sequence_move;
+ int decks_num = instance->decks_num;
+
+ cards_under_sequences = 0;
+ num_freestacks = 0;
+ seqs_over_renegade_cards = 0;
+ for(a=0;a<stacks_num;a++)
+ {
+ cards_num = fcs_stack_len(state, a);
+ if (cards_num == 0)
+ {
+ num_freestacks++;
+ }
+
+ if (cards_num <= 1)
+ {
+ continue;
+ }
+
+ c = cards_num-2;
+ this_card = fcs_stack_card(state, a, c+1);
+ prev_card = fcs_stack_card(state, a, c);
+ while ((c >= 0) && fcs_is_parent_card(this_card,prev_card))
+ {
+ c--;
+ this_card = prev_card;
+ if (c>=0)
+ {
+ prev_card = fcs_stack_card(state, a, c);
+ }
+ }
+ cards_under_sequences += pow(c+1, FCS_A_STAR_CARDS_UNDER_SEQUENCES_EXPONENT);
+ if (c >= 0)
+ {
+ seqs_over_renegade_cards +=
+ ((unlimited_sequence_move) ?
+ 1 :
+ pow(cards_num-c-1, FCS_A_STAR_SEQS_OVER_RENEGADE_CARDS_EXPONENT)
+ );
+ }
+ }
+
+ ret += ((soft_thread->a_star_initial_cards_under_sequences - cards_under_sequences)
+ / soft_thread->a_star_initial_cards_under_sequences) * a_star_weights[FCS_A_STAR_WEIGHT_CARDS_UNDER_SEQUENCES];
+
+ ret += (seqs_over_renegade_cards /
+ pow(decks_num*52, FCS_A_STAR_SEQS_OVER_RENEGADE_CARDS_EXPONENT) )
+ * a_star_weights[FCS_A_STAR_WEIGHT_SEQS_OVER_RENEGADE_CARDS];
+
+ num_cards_in_founds = 0;
+ for(a=0;a<(decks_num<<2);a++)
+ {
+ num_cards_in_founds += fcs_foundation_value(state, a);
+ }
+
+ ret += ((double)num_cards_in_founds/(decks_num*52)) * a_star_weights[FCS_A_STAR_WEIGHT_CARDS_OUT];
+
+ num_freecells = 0;
+ for(a=0;a<freecells_num;a++)
+ {
+ if (fcs_freecell_card_num(state,a) == 0)
+ {
+ num_freecells++;
+ }
+ }
+
+ if (instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD)
+ {
+ if (unlimited_sequence_move)
+ {
+ temp = (((double)num_freecells+num_freestacks)/(freecells_num+instance->stacks_num));
+ }
+ else
+ {
+ temp = (((double)((num_freecells+1)<<num_freestacks)) / ((freecells_num+1)<<(instance->stacks_num)));
+ }
+ }
+ else
+ {
+ if (unlimited_sequence_move)
+ {
+ temp = (((double)num_freecells)/freecells_num);
+ }
+ else
+ {
+ temp = 0;
+ }
+ }
+
+ ret += (temp * a_star_weights[FCS_A_STAR_WEIGHT_MAX_SEQUENCE_MOVE]);
+
+ if (ptr_state_with_locations->depth <= 20000)
+ {
+ ret += ((20000 - ptr_state_with_locations->depth)/20000.0) * a_star_weights[FCS_A_STAR_WEIGHT_DEPTH];
+ }
+
+ return (int)(ret*INT_MAX);
+}
+
+
+
+
+/*
+ freecell_solver_a_star_or_bfs_do_solve_or_resume() is the main event
+ loop of the A* And BFS scans. It is quite simple as all it does is
+ extract elements out of the queue or priority queue and run all the test
+ of them.
+
+ It goes on in this fashion until the final state was reached or
+ there are no more states in the queue.
+*/
+
+#define myreturn(ret_value) \
+ /* Free the memory that was allocated by the \
+ * derived states list */ \
+ if (derived.states != NULL) \
+ { \
+ free(derived.states); \
+ } \
+ \
+ soft_thread->bfs_queue_last_item = bfs_queue_last_item; \
+ \
+ return (ret_value);
+
+
+int freecell_solver_a_star_or_bfs_do_solve_or_resume(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations_orig,
+ int resume
+ )
+{
+ freecell_solver_hard_thread_t * hard_thread = soft_thread->hard_thread;
+ freecell_solver_instance_t * instance = hard_thread->instance;
+
+ fcs_state_with_locations_t * ptr_state_with_locations;
+ int num_freestacks, num_freecells;
+ fcs_states_linked_list_item_t * save_item;
+ int a;
+ int check;
+ fcs_derived_states_list_t derived;
+ int derived_index;
+
+ int method;
+ int freecells_num, stacks_num;
+ int tests_order_num;
+ int * tests_order_tests;
+ int calc_real_depth = instance->calc_real_depth;
+ int soft_thread_id = soft_thread->id;
+ int is_a_complete_scan = soft_thread->is_a_complete_scan;
+ int to_reparent_states =
+ (instance->to_reparent_states ||
+ (soft_thread->method == FCS_METHOD_OPTIMIZE)
+ );
+ int scans_synergy = instance->scans_synergy;
+ fcs_states_linked_list_item_t * bfs_queue = soft_thread->bfs_queue;
+ PQUEUE * a_star_pqueue = soft_thread->a_star_pqueue;
+ fcs_states_linked_list_item_t * bfs_queue_last_item = soft_thread->bfs_queue_last_item;
+
+ derived.num_states = 0;
+ derived.max_num_states = 0;
+ derived.states = NULL;
+
+ tests_order_num = soft_thread->tests_order.num;
+ tests_order_tests = soft_thread->tests_order.tests;
+
+ if (!resume)
+ {
+ /* Initialize the first element to indicate it is the first */
+ ptr_state_with_locations_orig->parent = NULL;
+ ptr_state_with_locations_orig->moves_to_parent = NULL;
+ ptr_state_with_locations_orig->depth = 0;
+ }
+
+ ptr_state_with_locations = ptr_state_with_locations_orig;
+
+ method = soft_thread->method;
+ freecells_num = instance->freecells_num;
+ stacks_num = instance->stacks_num;
+
+ /* Continue as long as there are states in the queue or
+ priority queue. */
+ while ( ptr_state_with_locations != NULL)
+ {
+ /*
+ * If this is an optimization scan and the state being checked is not
+ * in the original solution path - move on to the next state
+ * */
+ if ((method == FCS_METHOD_OPTIMIZE) && (!(ptr_state_with_locations->visited & FCS_VISITED_IN_SOLUTION_PATH)))
+ {
+ goto label_next_state;
+ }
+
+ /*
+ * It the state has already been visited - move on to the next
+ * state.
+ * */
+ if ((method == FCS_METHOD_OPTIMIZE) ?
+ (ptr_state_with_locations->visited & FCS_VISITED_IN_OPTIMIZED_PATH) :
+ ((ptr_state_with_locations->visited & FCS_VISITED_DEAD_END) ||
+ (is_scan_visited(ptr_state_with_locations, soft_thread_id)))
+ )
+ {
+ goto label_next_state;
+ }
+
+ /* Count the free-cells */
+ num_freecells = 0;
+ for(a=0;a<freecells_num;a++)
+ {
+ if (fcs_freecell_card_num(state, a) == 0)
+ {
+ num_freecells++;
+ }
+ }
+
+ /* Count the number of unoccupied stacks */
+
+ num_freestacks = 0;
+ for(a=0;a<stacks_num;a++)
+ {
+ if (fcs_stack_len(state, a) == 0)
+ {
+ num_freestacks++;
+ }
+ }
+
+ if ((instance->debug_iter_output) && (!resume))
+ {
+#ifdef DEBUG
+ printf("ST Name: %s\n", soft_thread->name);
+#endif
+ instance->debug_iter_output_func(
+ (void*)instance->debug_iter_output_context,
+ instance->num_times,
+ ptr_state_with_locations->depth,
+ (void*)instance,
+ ptr_state_with_locations,
+ ((ptr_state_with_locations->parent == NULL) ?
+ 0 :
+ ptr_state_with_locations->parent->visited_iter
+ )
+ );
+ }
+
+
+ if ((num_freestacks == stacks_num) && (num_freecells == freecells_num))
+ {
+ instance->final_state = ptr_state_with_locations;
+
+ myreturn(FCS_STATE_WAS_SOLVED);
+ }
+
+ calculate_real_depth(ptr_state_with_locations);
+
+ /* Do all the tests at one go, because that the way it should be
+ done for BFS and A*
+ */
+ derived.num_states = 0;
+ for(a=0 ;
+ a < tests_order_num;
+ a++)
+ {
+ check = freecell_solver_sfs_tests[tests_order_tests[a] & FCS_TEST_ORDER_NO_FLAGS_MASK] (
+ soft_thread,
+ ptr_state_with_locations,
+ num_freestacks,
+ num_freecells,
+ &derived,
+ /*
+ * We want to reparent the new states, only if this
+ * is an optimization scan.
+ * */
+ to_reparent_states
+ );
+ if ((check == FCS_STATE_BEGIN_SUSPEND_PROCESS) ||
+ (check == FCS_STATE_EXCEEDS_MAX_NUM_TIMES) ||
+ (check == FCS_STATE_SUSPEND_PROCESS))
+ {
+ /* Save the current position in the scan */
+ soft_thread->first_state_to_check = ptr_state_with_locations;
+
+ myreturn(FCS_STATE_SUSPEND_PROCESS);
+ }
+ }
+
+ if (check_if_limits_exceeded())
+
+ {
+ soft_thread->first_state_to_check = ptr_state_with_locations;
+
+ myreturn(FCS_STATE_SUSPEND_PROCESS);
+ }
+
+
+ if (is_a_complete_scan)
+ {
+ ptr_state_with_locations->visited |= FCS_VISITED_ALL_TESTS_DONE;
+ }
+
+ /* Increase the number of iterations by one .
+ * */
+ {
+ instance->num_times++;
+ hard_thread->num_times++;
+ }
+
+ /* Insert all the derived states into the PQ or Queue */
+
+ for(derived_index = 0 ; derived_index < derived.num_states ; derived_index++)
+ {
+ if (method == FCS_METHOD_A_STAR)
+ {
+ freecell_solver_a_star_enqueue_state(
+ soft_thread,
+ derived.states[derived_index]
+ );
+ }
+ else
+ {
+ freecell_solver_bfs_enqueue_state(
+ soft_thread,
+ derived.states[derived_index]
+ );
+ }
+ }
+
+ if (method == FCS_METHOD_OPTIMIZE)
+ {
+ ptr_state_with_locations->visited |= FCS_VISITED_IN_OPTIMIZED_PATH;
+ }
+ else
+ {
+ set_scan_visited(ptr_state_with_locations, soft_thread_id);
+
+ if (derived.num_states == 0)
+ {
+ if (is_a_complete_scan)
+ {
+ mark_as_dead_end(ptr_state_with_locations);
+ }
+ }
+ }
+
+ ptr_state_with_locations->visited_iter = instance->num_times-1;
+
+label_next_state:
+
+ /*
+ Extract the next item in the queue/priority queue.
+ */
+ if ((method == FCS_METHOD_BFS) || (method == FCS_METHOD_OPTIMIZE))
+ {
+ save_item = bfs_queue->next;
+ if (save_item != bfs_queue_last_item)
+ {
+ ptr_state_with_locations = save_item->s;
+ bfs_queue->next = save_item->next;
+ free(save_item);
+ }
+ else
+ {
+ ptr_state_with_locations = NULL;
+ }
+ }
+ else
+ {
+ /* It is an A* scan */
+ ptr_state_with_locations = freecell_solver_PQueuePop(a_star_pqueue);
+ }
+ resume = 0;
+ }
+
+ myreturn(FCS_STATE_IS_NOT_SOLVEABLE);
+}
+
+#undef myreturn
+
+#undef state
diff --git a/kpat/freecell-solver/simpsim.c b/kpat/freecell-solver/simpsim.c
new file mode 100644
index 00000000..f603ba39
--- /dev/null
+++ b/kpat/freecell-solver/simpsim.c
@@ -0,0 +1,1716 @@
+/*
+ * simpsim.c - a module that contains Simple Simon Moves.
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2001
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+
+#include <stdio.h>
+
+#include "fcs.h"
+
+#include "tests.h"
+
+#include "ms_ca.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+
+#define fcs_is_ss_false_parent(parent, child) \
+ (fcs_card_card_num(parent) == fcs_card_card_num(child)+1)
+
+#define fcs_suit_is_ss_true_parent(parent_suit, child_suit) \
+ ((parent_suit) == (child_suit))
+
+#define fcs_is_ss_true_parent(parent, child) \
+ ( \
+ fcs_is_ss_false_parent(parent,child) && \
+ (fcs_suit_is_ss_true_parent(fcs_card_suit(parent),fcs_card_suit(child))) \
+ )
+
+/*
+ * Those are some macros to make it easier for the programmer.
+ * */
+#define state_with_locations (*ptr_state_with_locations)
+#define state (ptr_state_with_locations->s)
+#define new_state_with_locations (*ptr_new_state_with_locations)
+#define new_state (ptr_new_state_with_locations->s)
+
+
+
+int freecell_solver_sfs_simple_simon_move_sequence_to_founds(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+
+
+ fcs_move_t temp_move;
+
+ int check;
+
+ fcs_card_t temp_card;
+
+ /*
+ * stack - the stack index from which to move cards to the founds.
+ * cards_num - the number of cards in "stack"
+ * suit - the suit of the complete sequence
+ * a - the height of the card
+ * */
+ int stack, cards_num, suit, a;
+ /*
+ * card - the current card (at height a)
+ * above_card - the card above it.
+ * */
+ fcs_card_t card, above_card;
+
+ int state_stacks_num;
+ tests_define_accessors();
+
+ state_stacks_num = instance->stacks_num;
+
+
+ for(stack=0;stack<state_stacks_num;stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+ if (cards_num >= 13)
+ {
+ card = fcs_stack_card(state,stack,cards_num-1);
+
+ /* Check if the top 13 cards are a sequence */
+
+ for(a=2;a<=13;a++)
+ {
+ above_card = fcs_stack_card(state,stack,cards_num-a);
+ if (fcs_is_ss_true_parent(above_card, card))
+ {
+ /* Do nothing - the card is OK for a propert sequence*/
+ }
+ else
+ {
+ break;
+ }
+ card = above_card;
+ }
+ if (a == 14)
+ {
+ /* We can move this sequence up there */
+
+ sfs_check_state_begin();
+
+ my_copy_stack(stack);
+
+ suit = fcs_card_suit(card);
+ for(a=0;a<13;a++)
+ {
+ fcs_pop_stack_card(new_state, stack, temp_card);
+ fcs_increment_foundation(new_state, suit);
+ }
+
+
+ fcs_move_init(temp_move);
+ fcs_move_set_type(temp_move, FCS_MOVE_TYPE_SEQ_TO_FOUNDATION);
+ fcs_move_set_src_stack(temp_move, stack);
+ fcs_move_set_foundation(temp_move,suit);
+ fcs_move_stack_push(moves,temp_move);
+
+ sfs_check_state_end();
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+int freecell_solver_sfs_simple_simon_move_sequence_to_true_parent(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+
+
+ fcs_move_t temp_move;
+
+ int check;
+
+ /*
+ * stack - the source stack index on which the sequence currently resides.
+ * cards_num - the number of cards in "stack".
+ * suit - the suit of the current card
+ * a - a temporary variable that designates a card height
+ * */
+ int stack, cards_num, suit, a;
+ /*
+ * h - the current height in stack
+ * */
+ int h;
+ /*
+ * card - the current card (at height h)
+ * above_card - the card above it.
+ * dest_card - the destination card on which to put the sequence
+ * */
+ fcs_card_t card, temp_card, dest_card;
+ /*
+ * card_num - the card number (i.e: A, 2 ,3 ... K) of the card, or
+ * its previous one.
+ * num_true_seqs - the number of true sequences (i.e: sequences of a
+ * unified suit) in the source sequence.
+ * ds - the destination stack index.
+ * dest_cards_num - the number of cards in "ds".
+ * */
+ int card_num, num_true_seqs, ds, dest_cards_num ;
+
+ int state_stacks_num;
+ tests_define_accessors();
+
+ state_stacks_num = instance->stacks_num;
+
+
+ for(stack=0;stack<state_stacks_num;stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+ if (cards_num > 0)
+ {
+ /* Loop on the cards in the stack and try to look for a true
+ * parent on top one of the stacks */
+ card = fcs_stack_card(state,stack,cards_num-1);
+ card_num = fcs_card_card_num(card);
+ suit = fcs_card_suit(card);
+ num_true_seqs = 1;
+
+ for(h=cards_num-2;h>=-1;h--)
+ {
+ for(ds=0;ds<state_stacks_num;ds++)
+ {
+ if (ds == stack)
+ {
+ continue;
+ }
+
+ dest_cards_num = fcs_stack_len(state,ds);
+ if (dest_cards_num > 0)
+ {
+ dest_card = fcs_stack_card(state, ds, dest_cards_num-1);
+ if ((fcs_card_suit(dest_card) == suit) &&
+ (fcs_card_card_num(dest_card) == (card_num+1))
+ )
+ {
+ /* This is a suitable parent - let's check if we
+ * have enough empty stacks to make the move feasible */
+ if (calc_max_sequence_move(0, num_freestacks) >= num_true_seqs)
+ {
+ /* We can do it - so let's move */
+
+ sfs_check_state_begin();
+
+ my_copy_stack(stack);
+ my_copy_stack(ds);
+
+
+ fcs_move_sequence(ds, stack, h+1, cards_num-1, a);
+ sfs_check_state_end();
+
+ }
+ }
+ }
+ }
+
+ /* Stop if we reached the bottom of the stack */
+ if (h == -1)
+ {
+ break;
+ }
+
+ card = fcs_stack_card(state,stack,h);
+ /* If this is no longer a sequence - move to the next stack */
+ if (fcs_card_card_num(card) != card_num+1)
+ {
+ break;
+ }
+ if (! fcs_suit_is_ss_true_parent(suit, fcs_card_suit(card)))
+ {
+ num_true_seqs++;
+ }
+ card_num = fcs_card_card_num(card);
+ suit = fcs_card_suit(card);
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+int freecell_solver_sfs_simple_simon_move_whole_stack_sequence_to_false_parent(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+
+
+ fcs_move_t temp_move;
+
+ int check;
+
+ /*
+ * stack - the source stack index
+ * cards_num - number of cards in "stack"
+ * ds - the dest stack index
+ * dest_cards_num - number of cards in "ds".
+ * card - the current card
+ * card_num - its card number
+ * suit - its suit
+ * dest_card - the card at the top of "ds".
+ * h - the height of the current card on "stack"
+ * num_true_seqs - the number of true sequences on the current
+ * false sequence
+ * */
+ int stack, cards_num, suit, a;
+ fcs_card_t card, temp_card, dest_card;
+ int card_num, num_true_seqs, h, ds, dest_cards_num ;
+
+ int state_stacks_num;
+ tests_define_accessors();
+
+ state_stacks_num = instance->stacks_num;
+
+ for(stack=0;stack<state_stacks_num;stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+ if (cards_num > 0)
+ {
+ card = fcs_stack_card(state,stack,cards_num-1);
+ card_num = fcs_card_card_num(card);
+ suit = fcs_card_suit(card);
+ num_true_seqs = 1;
+
+ /* Stop if we reached the bottom of the stack */
+ for(h=cards_num-2;h>-1;h--)
+ {
+ card = fcs_stack_card(state,stack,h);
+ /* If this is no longer a sequence - move to the next stack */
+ if (fcs_card_card_num(card) != card_num+1)
+ {
+ break;
+ }
+ if (fcs_card_suit(card) != suit)
+ {
+ num_true_seqs++;
+ }
+ card_num = fcs_card_card_num(card);
+ suit = fcs_card_suit(card);
+ }
+ /* This means that the loop exited prematurely and the stack does
+ * not contain a sequence. */
+ if (h != -1)
+ {
+ continue;
+ }
+
+ for(ds=0;ds<state_stacks_num;ds++)
+ {
+ dest_cards_num = fcs_stack_len(state,ds);
+ if (dest_cards_num > 0)
+ {
+ dest_card = fcs_stack_card(state, ds, dest_cards_num-1);
+ if (
+ (fcs_is_ss_false_parent(dest_card, card))
+ )
+ {
+ /* This is a suitable parent - let's check if we
+ * have enough empty stacks to make the move feasible */
+ if (calc_max_sequence_move(0, num_freestacks) >= num_true_seqs)
+ {
+ /* We can do it - so let's move */
+
+ sfs_check_state_begin();
+
+ my_copy_stack(stack);
+ my_copy_stack(ds);
+
+
+ fcs_move_sequence(ds, stack, h+1, cards_num-1, a);
+ sfs_check_state_end();
+
+ }
+ }
+ }
+ }
+
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+
+int freecell_solver_sfs_simple_simon_move_sequence_to_true_parent_with_some_cards_above(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+
+
+ fcs_move_t temp_move;
+ int check;
+
+ /*
+ * stack - the source stack index
+ * cards_num - the number of cards in "stack"
+ * h - the height of the current card in "stack"
+ * card - the card in height "h"
+ * suit - its suit
+ * card_num - its card number
+ * ds - the destionation stack index
+ * dest_cards_num - the number of cards in "ds"
+ * dc - the index of the current card in "ds".
+ * num_separate_false_seqs - this variable tells how many distinct false
+ * sequences exist above the true parent
+ * above_num_true_seqs[] - the number of true sequences in each false
+ * sequence
+ * seq_points[] - the separation points of the false sequences (i.e: where
+ * they begin and end)
+ * stacks_map[] - a boolean map that indicates if one can place a card
+ * on this stack or is it already taken.
+ * junk_move_to_stacks[] - the stacks to move each false sequence of the
+ * junk to.
+ * false_seq_index - an iterator to hold the index of the current false
+ * sequence.
+ * after_junk_num_freestacks - this variable holds the number of stacks
+ * that remained unoccupied during and after the process of moving
+ * the junk sequences to different stacks.
+ *
+ * */
+ int stack, cards_num, suit, a;
+ fcs_card_t card, temp_card, dest_card;
+ int card_num, above_num_true_seqs[MAX_NUM_CARDS_IN_A_STACK], h, ds, dest_cards_num ;
+ int dc;
+ int seq_points[MAX_NUM_CARDS_IN_A_STACK];
+ int num_separate_false_seqs;
+ int false_seq_index;
+ int num_true_seqs;
+ int stacks_map[MAX_NUM_STACKS];
+ int after_junk_num_freestacks;
+ int junk_move_to_stacks[MAX_NUM_STACKS];
+
+ int state_stacks_num;
+ tests_define_accessors();
+
+ state_stacks_num = instance->stacks_num;
+
+ for(stack=0;stack<state_stacks_num;stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+ if (cards_num > 0)
+ {
+ card = fcs_stack_card(state,stack,cards_num-1);
+ card_num = fcs_card_card_num(card);
+ suit = fcs_card_suit(card);
+
+ num_true_seqs = 1;
+
+
+ for(h=cards_num-2;h>=-1;h--)
+ {
+ for(ds=0;ds<state_stacks_num;ds++)
+ {
+ if (ds == stack)
+ {
+ continue;
+ }
+
+ dest_cards_num = fcs_stack_len(state,ds);
+ if (dest_cards_num > 0)
+ {
+ for(dc=dest_cards_num-1;dc>=0;dc--)
+ {
+ dest_card = fcs_stack_card(state, ds, dc);
+ if ((fcs_card_suit(dest_card) == suit) &&
+ (fcs_card_card_num(dest_card) == (card_num+1))
+ )
+ {
+ /* This is a suitable parent - let's check if there's a sequence above it. */
+
+ /*
+ * above_c - the height of the card that is to be checked.
+ * above_card - the card at height above_c+1
+ * up_above_card - the card at height above_c
+ *
+ * */
+ int above_c;
+ fcs_card_t above_card, up_above_card;
+
+ num_separate_false_seqs = 0;
+ above_card = fcs_stack_card(state, ds, dest_cards_num-1);
+ above_num_true_seqs[num_separate_false_seqs] = 1;
+ for(above_c = dest_cards_num-2 ;
+ above_c > dc ;
+ above_c--
+ )
+ {
+ up_above_card = fcs_stack_card(state, ds, above_c);
+ if (! fcs_is_ss_false_parent(up_above_card, above_card))
+ {
+ seq_points[num_separate_false_seqs++] = above_c+1;
+ above_num_true_seqs[num_separate_false_seqs] = 1;
+ }
+ above_num_true_seqs[num_separate_false_seqs] += ! (fcs_card_suit(up_above_card) == fcs_card_suit(above_card));
+ above_card = up_above_card;
+ }
+
+ if (dc < dest_cards_num - 1)
+ {
+ seq_points[num_separate_false_seqs++] = above_c+1;
+ }
+
+ for(a=0;a<state_stacks_num;a++)
+ {
+ stacks_map[a] = 0;
+ }
+ stacks_map[stack] = 1;
+ stacks_map[ds] = 1;
+
+ after_junk_num_freestacks = num_freestacks;
+
+ for(false_seq_index=0;false_seq_index<num_separate_false_seqs;false_seq_index++)
+ {
+ /* Find a suitable place to put it */
+
+ /*
+ * clear_junk_dest_stack is the stack to move this particular junk sequence to.
+ * */
+ int clear_junk_dest_stack = -1;
+
+
+ /* Let's try to find a suitable parent on top one of the stacks */
+ for(clear_junk_dest_stack=0;
+ clear_junk_dest_stack < state_stacks_num;
+ clear_junk_dest_stack++
+ )
+ {
+ int clear_junk_stack_len;
+ clear_junk_stack_len = fcs_stack_len(state, clear_junk_dest_stack);
+
+ if ((clear_junk_stack_len > 0) && (stacks_map[clear_junk_dest_stack] == 0))
+ {
+ fcs_card_t clear_junk_dest_card;
+
+ clear_junk_dest_card = fcs_stack_card(state, clear_junk_dest_stack, clear_junk_stack_len-1);
+ if (fcs_is_ss_false_parent(clear_junk_dest_card, fcs_stack_card(state, ds, seq_points[false_seq_index])))
+ {
+ if (calc_max_sequence_move(0, after_junk_num_freestacks) >= above_num_true_seqs[false_seq_index])
+ {
+ stacks_map[clear_junk_dest_stack] = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ if (clear_junk_dest_stack == state_stacks_num)
+ {
+ clear_junk_dest_stack = -1;
+ }
+
+ if (clear_junk_dest_stack == -1)
+ {
+ /* Check if there is a vacant stack */
+ if (num_freestacks > 0)
+ {
+ if (calc_max_sequence_move(0, after_junk_num_freestacks-1) >= above_num_true_seqs[false_seq_index])
+ {
+ /* Find an empty stack and designate it as the destination for the junk */
+ for(
+ clear_junk_dest_stack = 0;
+ clear_junk_dest_stack < state_stacks_num;
+ clear_junk_dest_stack++
+ )
+ {
+ if ((fcs_stack_len(state, clear_junk_dest_stack) == 0) && (stacks_map[clear_junk_dest_stack] == 0))
+ {
+ stacks_map[clear_junk_dest_stack] = 1;
+ break;
+ }
+ }
+ }
+ after_junk_num_freestacks--;
+ }
+ }
+
+ if ((clear_junk_dest_stack == -1))
+ {
+ break;
+ }
+ junk_move_to_stacks[false_seq_index] = clear_junk_dest_stack;
+ }
+
+ if (false_seq_index == num_separate_false_seqs)
+ {
+ if (calc_max_sequence_move(0, after_junk_num_freestacks) >= num_true_seqs)
+ {
+ /*
+ * We can do it - so let's move everything.
+ * Notice that we only put the child in a different stack
+ * then the parent and let it move to the parent in the
+ * next iteration of the program
+ * */
+
+ sfs_check_state_begin();
+
+ my_copy_stack(ds);
+ my_copy_stack(stack);
+
+
+ /* Move the junk cards to their place */
+
+ for(false_seq_index=0;
+ false_seq_index<num_separate_false_seqs;
+ false_seq_index++
+ )
+ {
+ /*
+ * start and end are the start and end heights of the sequence that is to be moved.
+ * */
+ int start = seq_points[false_seq_index];
+ int end = ((false_seq_index == 0) ? (dest_cards_num-1) : (seq_points[false_seq_index-1]-1));
+
+ my_copy_stack(junk_move_to_stacks[false_seq_index]);
+
+ fcs_move_sequence(junk_move_to_stacks[false_seq_index], ds, start, end, a);
+
+ }
+
+ /* Move the source seq on top of the dest seq */
+ fcs_move_sequence(ds, stack, h+1, cards_num-1, a);
+
+ sfs_check_state_end();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Stop if we reached the bottom of the stack */
+ if (h == -1)
+ {
+ break;
+ }
+ /* If this is no longer a sequence - move to the next stack */
+ if (fcs_stack_card_num(state,stack, h) != card_num+1)
+ {
+ break;
+ }
+ card = fcs_stack_card(state,stack,h);
+ if (! fcs_suit_is_ss_true_parent(suit, fcs_card_suit(card)))
+ {
+ num_true_seqs++;
+ }
+ card_num = fcs_card_card_num(card);
+ suit = fcs_card_suit(card);
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+int freecell_solver_sfs_simple_simon_move_sequence_with_some_cards_above_to_true_parent(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+
+
+ fcs_move_t temp_move;
+
+ int stack, cards_num, suit, a;
+ fcs_card_t card, temp_card, dest_card;
+ int card_num, num_true_seqs, ds, dest_cards_num ;
+ int check;
+ int sc, num_separate_false_seqs, above_num_true_seqs[MAX_NUM_CARDS_IN_A_STACK];
+ int seq_points[MAX_NUM_CARDS_IN_A_STACK];
+ int stacks_map[MAX_NUM_STACKS];
+ int after_junk_num_freestacks;
+ int false_seq_index;
+ int junk_move_to_stacks[MAX_NUM_CARDS_IN_A_STACK];
+
+ int state_stacks_num;
+ tests_define_accessors();
+
+ state_stacks_num = instance->stacks_num;
+
+ for(stack=0;stack<state_stacks_num;stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+ if (cards_num > 0)
+ {
+ for( sc = cards_num-1 ; sc >= 0 ; sc-- )
+ {
+ int above_c;
+ fcs_card_t above_card, up_above_card;
+ int end_of_src_seq;
+
+ card = fcs_stack_card(state, stack, sc);
+ suit = fcs_card_suit(card);
+ card_num = fcs_card_card_num(card);
+
+ num_true_seqs = 1;
+
+ for (end_of_src_seq = sc+1; end_of_src_seq < cards_num ; end_of_src_seq++)
+ {
+ above_card = fcs_stack_card(state, stack, end_of_src_seq);
+ if (!fcs_is_ss_false_parent(card, above_card))
+ {
+ break;
+ }
+ if (fcs_card_suit(above_card) != fcs_card_suit(card))
+ {
+ num_true_seqs++;
+ }
+ card = above_card;
+ }
+
+ if (end_of_src_seq == cards_num)
+ {
+ continue;
+ }
+
+ /* Split the cards above it into false sequences */
+
+ num_separate_false_seqs = 0;
+ above_card = fcs_stack_card(state, stack, cards_num-1);
+ above_num_true_seqs[num_separate_false_seqs] = 1;
+ for(above_c = cards_num-2 ;
+ above_c > end_of_src_seq-1 ;
+ above_c--
+ )
+ {
+ up_above_card = fcs_stack_card(state, stack, above_c);
+ if (! fcs_is_ss_false_parent(up_above_card, above_card))
+ {
+ seq_points[num_separate_false_seqs++] = above_c+1;
+ above_num_true_seqs[num_separate_false_seqs] = 1;
+ }
+ above_num_true_seqs[num_separate_false_seqs] += ! (fcs_card_suit(up_above_card) == fcs_card_suit(above_card));
+ above_card = up_above_card;
+ }
+
+ if (end_of_src_seq-1 < cards_num-1)
+ {
+ seq_points[num_separate_false_seqs++] = above_c+1;
+ }
+
+ for(ds=0;ds<state_stacks_num;ds++)
+ {
+ if (ds == stack)
+ {
+ continue;
+ }
+
+ dest_cards_num = fcs_stack_len(state,ds);
+ if (dest_cards_num > 0)
+ {
+ dest_card = fcs_stack_card(state, ds, dest_cards_num-1);
+ if ((fcs_card_suit(dest_card) == suit) &&
+ (fcs_card_card_num(dest_card) == (card_num+1))
+ )
+ {
+ /* This is a suitable parent - let's check if we
+ * have enough empty stacks to make the move feasible */
+
+ for(a=0;a<state_stacks_num;a++)
+ {
+ stacks_map[a] = 0;
+ }
+ stacks_map[stack] = 1;
+ stacks_map[ds] = 1;
+
+ after_junk_num_freestacks = num_freestacks;
+
+ for(false_seq_index=0;false_seq_index<num_separate_false_seqs;false_seq_index++)
+ {
+ /* Find a suitable place to put it */
+ int clear_junk_dest_stack = -1;
+
+
+ /* Let's try to find a suitable parent on top one of the stacks */
+ for(clear_junk_dest_stack=0;
+ clear_junk_dest_stack < state_stacks_num;
+ clear_junk_dest_stack++
+ )
+ {
+ int clear_junk_stack_len;
+ clear_junk_stack_len = fcs_stack_len(state, clear_junk_dest_stack);
+
+ if ((clear_junk_stack_len > 0) && (stacks_map[clear_junk_dest_stack] == 0))
+ {
+ fcs_card_t clear_junk_dest_card;
+
+ clear_junk_dest_card = fcs_stack_card(state, clear_junk_dest_stack, clear_junk_stack_len-1);
+ if (fcs_is_ss_false_parent(clear_junk_dest_card, fcs_stack_card(state, stack, seq_points[false_seq_index])))
+ {
+ if (calc_max_sequence_move(0, after_junk_num_freestacks) >= above_num_true_seqs[false_seq_index])
+ {
+ stacks_map[clear_junk_dest_stack] = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ if (clear_junk_dest_stack == state_stacks_num)
+ {
+ clear_junk_dest_stack = -1;
+ }
+
+ if (clear_junk_dest_stack == -1)
+ {
+ /* Check if there is a vacant stack */
+ if (num_freestacks > 0)
+ {
+ if (calc_max_sequence_move(0, after_junk_num_freestacks-1) >= above_num_true_seqs[false_seq_index])
+ {
+ /* Find an empty stack and designate it as the destination for the junk */
+ for(
+ clear_junk_dest_stack = 0;
+ clear_junk_dest_stack < state_stacks_num;
+ clear_junk_dest_stack++
+ )
+ {
+ if ((fcs_stack_len(state, clear_junk_dest_stack) == 0) && (stacks_map[clear_junk_dest_stack] == 0))
+ {
+ stacks_map[clear_junk_dest_stack] = 1;
+ break;
+ }
+ }
+ }
+ after_junk_num_freestacks--;
+ }
+ }
+
+ if ((clear_junk_dest_stack == -1))
+ {
+ break;
+ }
+ junk_move_to_stacks[false_seq_index] = clear_junk_dest_stack;
+ }
+
+ if (false_seq_index == num_separate_false_seqs)
+ {
+ if (calc_max_sequence_move(0, after_junk_num_freestacks) > num_true_seqs)
+ {
+ sfs_check_state_begin();
+
+ my_copy_stack(stack);
+ my_copy_stack(ds);
+
+
+
+ /* Let's boogie - we can move everything */
+
+ /* Move the junk cards to their place */
+
+ for(false_seq_index=0;
+ false_seq_index<num_separate_false_seqs;
+ false_seq_index++
+ )
+ {
+ int start;
+ int end;
+
+ int src_stack;
+
+ {
+ start = seq_points[false_seq_index];
+ end = ((false_seq_index == 0) ? (cards_num-1) : (seq_points[false_seq_index-1]-1));
+ src_stack = stack;
+ }
+
+ my_copy_stack(junk_move_to_stacks[false_seq_index]);
+
+ fcs_move_sequence(junk_move_to_stacks[false_seq_index], src_stack, start, end, a);
+ }
+
+ fcs_move_sequence(ds, stack, sc, end_of_src_seq-1, a);
+
+ sfs_check_state_end();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+int freecell_solver_sfs_simple_simon_move_sequence_with_junk_seq_above_to_true_parent_with_some_cards_above(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+
+
+ fcs_move_t temp_move;
+
+ int check;
+
+ /*
+ * stack - the source stack index
+ * cards_num - the number of cards in "stack"
+ * h - the height of the current card in "stack".
+ * card - the current card in "stack"
+ * suit - its suit
+ * card_num - its card number
+ * ds - the index of the destination stack
+ * dest_cards_num - the number of cards in "ds".
+ * dc - the height of the current card in "ds".
+ * num_separate_false_seqs - the number of false sequences
+ * seq_points[] - the places in which the false sequences of the junk begin
+ * and end
+ * false_seq_index - an iterator that marks the index of the current
+ * false sequence
+ * stacks_map[] - a map of booleans that indicates if one can place a card
+ * on this stack or is already taken.
+ * above_num_true_seqs[] - the number of true sequences in each false
+ * sequence
+ * num_src_junk_true_seqs - the number of true seqs in the false seq above
+ * the source card.
+ * end_of_junk - the height marking the end of the source junk.
+ * num_true_seqs - the number of true sequences in the false seq which we
+ * wish to move.
+ * */
+ int stack, cards_num, suit, a;
+ fcs_card_t card, temp_card, dest_card;
+ int card_num, above_num_true_seqs[MAX_NUM_CARDS_IN_A_STACK], h, ds, dest_cards_num ;
+
+ int dc;
+ int seq_points[MAX_NUM_CARDS_IN_A_STACK];
+ int num_separate_false_seqs;
+ int false_seq_index;
+ int stacks_map[MAX_NUM_STACKS];
+ int after_junk_num_freestacks;
+ int junk_move_to_stacks[MAX_NUM_STACKS];
+ int num_src_junk_true_seqs;
+ int end_of_junk;
+ int num_true_seqs;
+
+ int state_stacks_num;
+ tests_define_accessors();
+
+ state_stacks_num = instance->stacks_num;
+
+ for(stack=0;stack<state_stacks_num;stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+ if (cards_num > 0)
+ {
+ card = fcs_stack_card(state,stack,cards_num-1);
+ card_num = fcs_card_card_num(card);
+ suit = fcs_card_suit(card);
+ num_src_junk_true_seqs = 1;
+
+
+ for(h=cards_num-2;h>=-1;h--)
+ {
+ if (h == -1)
+ {
+ break;
+ }
+ card = fcs_stack_card(state, stack, h);
+ if (fcs_card_card_num(card) != card_num+1)
+ {
+ break;
+ }
+ if (fcs_card_suit(card) != suit)
+ {
+ num_src_junk_true_seqs++;
+ }
+ card_num = fcs_card_card_num(card);
+ suit = fcs_card_suit(card);
+ }
+
+ if (h != -1)
+ {
+ end_of_junk = h;
+ num_true_seqs = 1;
+
+ for(;h>=-1;h--)
+ {
+ if (h == -1)
+ {
+ break;
+ }
+ card = fcs_stack_card(state,stack,h);
+ if (fcs_card_card_num(card) != card_num+1)
+ {
+ break;
+ }
+ if (fcs_card_suit(card) != suit)
+ {
+ num_true_seqs++;
+ }
+ card_num = fcs_card_card_num(card);
+ suit = fcs_card_suit(card);
+ }
+
+ card_num = fcs_card_card_num(card);
+ suit = fcs_card_suit(card);
+
+ for(ds=0;ds<state_stacks_num;ds++)
+ {
+ if (ds == stack)
+ {
+ continue;
+ }
+
+ dest_cards_num = fcs_stack_len(state,ds);
+ /* At least a card with another card above it */
+ if (dest_cards_num > 1)
+ {
+ /* Start at the card below the top one, so we will
+ * make sure there's at least some junk above it
+ * */
+ for(dc=dest_cards_num-2;dc>=0;dc--)
+ {
+ dest_card = fcs_stack_card(state, ds, dc);
+ if ((fcs_card_suit(dest_card) == suit) &&
+ (fcs_card_card_num(dest_card) == (card_num+1))
+ )
+ {
+ /* This is a suitable parent - let's check if there's a sequence above it. */
+ int above_c;
+ fcs_card_t above_card, up_above_card;
+
+ num_separate_false_seqs = 0;
+ above_card = fcs_stack_card(state, ds, dest_cards_num-1);
+ above_num_true_seqs[num_separate_false_seqs] = 1;
+ for(above_c = dest_cards_num-2 ;
+ above_c > dc ;
+ above_c--
+ )
+ {
+ up_above_card = fcs_stack_card(state, ds, above_c);
+ if (! fcs_is_ss_false_parent(up_above_card, above_card))
+ {
+ seq_points[num_separate_false_seqs++] = above_c+1;
+ above_num_true_seqs[num_separate_false_seqs] = 1;
+ }
+ above_num_true_seqs[num_separate_false_seqs] += ! (fcs_card_suit(up_above_card) == fcs_card_suit(above_card));
+ above_card = up_above_card;
+ }
+
+ if (dc < dest_cards_num - 1)
+ {
+ seq_points[num_separate_false_seqs++] = above_c+1;
+ }
+
+ for(a=0;a<state_stacks_num;a++)
+ {
+ stacks_map[a] = 0;
+ }
+ stacks_map[stack] = 1;
+ stacks_map[ds] = 1;
+
+ after_junk_num_freestacks = num_freestacks;
+
+ for(false_seq_index=0;false_seq_index<num_separate_false_seqs+1;false_seq_index++)
+ {
+ /* Find a suitable place to put it */
+ int clear_junk_dest_stack = -1;
+
+ fcs_card_t the_card =
+ (
+ (false_seq_index == num_separate_false_seqs) ?
+ (fcs_stack_card(state, stack, end_of_junk+1)) :
+ (fcs_stack_card(state, ds, seq_points[false_seq_index]))
+ );
+
+ int the_num_true_seqs =
+ (
+ (false_seq_index == num_separate_false_seqs) ?
+ num_src_junk_true_seqs :
+ above_num_true_seqs[false_seq_index]
+ );
+
+ /* Let's try to find a suitable parent on top one of the stacks */
+ for(clear_junk_dest_stack=0;
+ clear_junk_dest_stack < state_stacks_num;
+ clear_junk_dest_stack++
+ )
+ {
+ int clear_junk_stack_len;
+ clear_junk_stack_len = fcs_stack_len(state, clear_junk_dest_stack);
+
+ if ((clear_junk_stack_len > 0) && (stacks_map[clear_junk_dest_stack] == 0))
+ {
+ fcs_card_t clear_junk_dest_card;
+
+ clear_junk_dest_card = fcs_stack_card(state, clear_junk_dest_stack, clear_junk_stack_len-1);
+ if (fcs_is_ss_false_parent(clear_junk_dest_card, the_card))
+ {
+ if (calc_max_sequence_move(0, after_junk_num_freestacks) >= the_num_true_seqs)
+ {
+ stacks_map[clear_junk_dest_stack] = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ if (clear_junk_dest_stack == state_stacks_num)
+ {
+ clear_junk_dest_stack = -1;
+ }
+
+ if (clear_junk_dest_stack == -1)
+ {
+ /* Check if there is a vacant stack */
+ if (num_freestacks > 0)
+ {
+ if (calc_max_sequence_move(0, after_junk_num_freestacks-1) >= the_num_true_seqs)
+ {
+ /* Find an empty stack and designate it as the destination for the junk */
+ for(
+ clear_junk_dest_stack = 0;
+ clear_junk_dest_stack < state_stacks_num;
+ clear_junk_dest_stack++
+ )
+ {
+ if ((fcs_stack_len(state, clear_junk_dest_stack) == 0) && (stacks_map[clear_junk_dest_stack] == 0))
+ {
+ stacks_map[clear_junk_dest_stack] = 1;
+ break;
+ }
+ }
+ }
+ after_junk_num_freestacks--;
+ }
+ }
+
+ if ((clear_junk_dest_stack == -1))
+ {
+ break;
+ }
+ junk_move_to_stacks[false_seq_index] = clear_junk_dest_stack;
+ }
+
+ if (false_seq_index == num_separate_false_seqs+1)
+ {
+ if (calc_max_sequence_move(0, after_junk_num_freestacks) >= num_true_seqs)
+ {
+ /* We can do it - so let's move everything */
+
+ sfs_check_state_begin();
+
+ my_copy_stack(stack);
+ my_copy_stack(ds);
+
+
+ /* Move the junk cards to their place */
+
+ for(false_seq_index=0;
+ false_seq_index<num_separate_false_seqs+1;
+ false_seq_index++
+ )
+ {
+ int start;
+ int end;
+
+ int src_stack;
+
+ if (false_seq_index == num_separate_false_seqs)
+ {
+ start = end_of_junk+1;
+ end = cards_num-1;
+ src_stack = stack;
+ }
+ else
+ {
+ start = seq_points[false_seq_index];
+ end = ((false_seq_index == 0) ? (dest_cards_num-1) : (seq_points[false_seq_index-1]-1));
+ src_stack = ds;
+ }
+
+ my_copy_stack(src_stack);
+
+ my_copy_stack(junk_move_to_stacks[false_seq_index]);
+
+ fcs_move_sequence(junk_move_to_stacks[false_seq_index], src_stack, start, end, a);
+ }
+
+ /* Move the source seq on top of the dest seq */
+ fcs_move_sequence(ds, stack, h, end_of_junk, a);
+
+ sfs_check_state_end();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+int freecell_solver_sfs_simple_simon_move_whole_stack_sequence_to_false_parent_with_some_cards_above(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+
+
+ fcs_move_t temp_move;
+
+ int check;
+
+ /*
+ * stack - the source stack index
+ * cards_num - the number of cards in "stack"
+ * h - the height of the current card in stack
+ * card - the current card
+ * suit - its suit
+ * card_num - its card number
+ * ds - the destination stack index.
+ * dest_cards_num - the number of cards in it.
+ * dc - the height of the card in "ds".
+ * num_separate_false_seqs - this variable tells how many distinct false
+ * sequences exist above the false parent
+ * above_num_true_seqs[] - the number of true sequences in each false
+ * sequence
+ * seq_points[] - the separation points of the false sequences (i.e: where
+ * they begin and end)
+ * stacks_map[] - a boolean map that indicates if one can place a card
+ * on this stack or is it already taken.
+ * junk_move_to_stacks[] - the stacks to move each false sequence of the
+ * junk to.
+ * false_seq_index - an iterator to hold the index of the current false
+ * sequence.
+ * after_junk_num_freestacks - a variable that holds the number of stacks
+ * that are left unoccupied as part of the junk disposal process.
+ *
+ * */
+ int stack, cards_num, suit, a;
+ fcs_card_t card, temp_card, dest_card;
+ int card_num, num_true_seqs, h, ds, dest_cards_num ;
+
+ int dc, num_separate_false_seqs, above_num_true_seqs[MAX_NUM_CARDS_IN_A_STACK];
+ int seq_points[MAX_NUM_CARDS_IN_A_STACK];
+ int stacks_map[MAX_NUM_STACKS];
+ int after_junk_num_freestacks;
+ int false_seq_index;
+ int junk_move_to_stacks[MAX_NUM_STACKS];
+
+ int state_stacks_num;
+ tests_define_accessors();
+
+ state_stacks_num = instance->stacks_num;
+
+ for(stack=0;stack<state_stacks_num;stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+ if (cards_num > 0)
+ {
+ card = fcs_stack_card(state,stack,cards_num-1);
+ card_num = fcs_card_card_num(card);
+ suit = fcs_card_suit(card);
+ num_true_seqs = 1;
+
+ for(h=cards_num-2;h>=-1;h--)
+ {
+ if (h == -1)
+ {
+ break;
+ }
+ card = fcs_stack_card(state,stack,h);
+ if (fcs_card_card_num(card) != card_num+1)
+ {
+ break;
+ }
+ if (fcs_card_suit(card) != suit)
+ {
+ num_true_seqs++;
+ }
+ card_num = fcs_card_card_num(card);
+ suit = fcs_card_suit(card);
+ }
+ if (h == -1)
+ {
+ for(ds=0;ds<state_stacks_num;ds++)
+ {
+ dest_cards_num = fcs_stack_len(state,ds);
+ if (dest_cards_num > 0)
+ {
+ for(dc=dest_cards_num-1;dc>=0;dc--)
+ {
+ dest_card = fcs_stack_card(state, ds, dc);
+ if (
+ (fcs_card_card_num(dest_card) == (card_num+1))
+ )
+ {
+ /* This is a suitable parent - let's check if there's a sequence above it. */
+ int above_c;
+ fcs_card_t above_card, up_above_card;
+
+ num_separate_false_seqs = 0;
+ above_card = fcs_stack_card(state, ds, dest_cards_num-1);
+ above_num_true_seqs[num_separate_false_seqs] = 1;
+ for(above_c = dest_cards_num-2 ;
+ above_c > dc ;
+ above_c--
+ )
+ {
+ up_above_card = fcs_stack_card(state, ds, above_c);
+ if (! fcs_is_ss_false_parent(up_above_card, above_card))
+ {
+ seq_points[num_separate_false_seqs++] = above_c+1;
+ above_num_true_seqs[num_separate_false_seqs] = 1;
+ }
+ above_num_true_seqs[num_separate_false_seqs] += ! (fcs_card_suit(up_above_card) == fcs_card_suit(above_card));
+ above_card = up_above_card;
+ }
+
+ if (dc < dest_cards_num - 1)
+ {
+ seq_points[num_separate_false_seqs++] = above_c+1;
+ }
+
+ for(a=0;a<state_stacks_num;a++)
+ {
+ stacks_map[a] = 0;
+ }
+ stacks_map[stack] = 1;
+ stacks_map[ds] = 1;
+
+ after_junk_num_freestacks = num_freestacks;
+
+ for(false_seq_index=0;false_seq_index<num_separate_false_seqs;false_seq_index++)
+ {
+ /* Find a suitable place to put it */
+ int clear_junk_dest_stack = -1;
+
+ fcs_card_t the_card =
+ (fcs_stack_card(state, ds, seq_points[false_seq_index]))
+ ;
+
+
+ int the_num_true_seqs =
+ above_num_true_seqs[false_seq_index];
+
+
+ /* Let's try to find a suitable parent on top one of the stacks */
+ for(clear_junk_dest_stack=0;
+ clear_junk_dest_stack < state_stacks_num;
+ clear_junk_dest_stack++
+ )
+ {
+ int clear_junk_stack_len;
+ clear_junk_stack_len = fcs_stack_len(state, clear_junk_dest_stack);
+
+ if ((clear_junk_stack_len > 0) && (stacks_map[clear_junk_dest_stack] == 0))
+ {
+ fcs_card_t clear_junk_dest_card;
+
+ clear_junk_dest_card = fcs_stack_card(state, clear_junk_dest_stack, clear_junk_stack_len-1);
+ if (fcs_is_ss_false_parent(clear_junk_dest_card, the_card))
+ {
+ if (calc_max_sequence_move(0, after_junk_num_freestacks) >= the_num_true_seqs)
+ {
+ stacks_map[clear_junk_dest_stack] = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ if (clear_junk_dest_stack == state_stacks_num)
+ {
+ clear_junk_dest_stack = -1;
+ }
+
+ if ((clear_junk_dest_stack == -1))
+ {
+ break;
+ }
+ junk_move_to_stacks[false_seq_index] = clear_junk_dest_stack;
+ }
+
+ if (false_seq_index == num_separate_false_seqs)
+ {
+ /* This is a suitable parent - let's check if we
+ * have enough empty stacks to make the move feasible */
+ if (calc_max_sequence_move(0, num_freestacks) >= num_true_seqs)
+ {
+ /* We can do it - so let's move */
+
+ sfs_check_state_begin();
+
+ my_copy_stack(stack);
+ my_copy_stack(ds);
+
+
+ /* Move the junk cards to their place */
+
+ for(false_seq_index=0;
+ false_seq_index<num_separate_false_seqs;
+ false_seq_index++
+ )
+ {
+ int start;
+ int end;
+
+ int src_stack;
+
+ {
+ start = seq_points[false_seq_index];
+ end = ((false_seq_index == 0) ? (dest_cards_num-1) : (seq_points[false_seq_index-1]-1));
+ src_stack = ds;
+ }
+
+ my_copy_stack(src_stack);
+ my_copy_stack(junk_move_to_stacks[false_seq_index]);
+
+ fcs_move_sequence( junk_move_to_stacks[false_seq_index], src_stack, start, end, a);
+ }
+
+ fcs_move_sequence( ds, stack, h+1, cards_num-1, a);
+
+ sfs_check_state_end();
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+int freecell_solver_sfs_simple_simon_move_sequence_to_parent_on_the_same_stack(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ )
+{
+ tests_declare_accessors();
+
+
+ fcs_move_t temp_move;
+
+ int check;
+
+ int stack, cards_num, pc, cc;
+ fcs_card_t parent_card, child_card;
+ int a;
+ int after_junk_num_freestacks;
+ int false_seq_index;
+ int child_seq_index;
+
+ fcs_card_t temp_card;
+
+ int state_stacks_num;
+ tests_define_accessors();
+
+ state_stacks_num = instance->stacks_num;
+
+ for(stack=0 ; stack < state_stacks_num ; stack++)
+ {
+ cards_num = fcs_stack_len(state, stack);
+ if (cards_num > 2)
+ {
+ /* Search for a parent card */
+ for(pc=0; pc < cards_num-1 ; pc++)
+ {
+ parent_card = fcs_stack_card(state, stack, pc);
+ if (
+ fcs_is_ss_true_parent(
+ parent_card,
+ fcs_stack_card(state, stack, pc+1)
+ )
+ )
+ {
+ continue;
+ }
+
+
+ for(cc = pc + 2 ; cc < cards_num ; cc++)
+ {
+ child_card = fcs_stack_card(state, stack, cc);
+ if (fcs_is_ss_true_parent(
+ parent_card,
+ child_card
+ )
+ )
+ {
+ /* We have a matching parent and child cards */
+#if 0
+ printf("Stack %i, Parent %i, Child %i\n", stack, pc, cc);
+ fflush(stdout);
+#endif
+
+ /*
+ * Now let's try to find stacks to place the cards above
+ * the child card.
+ * */
+
+ int above_num_true_seqs[MAX_NUM_CARDS_IN_A_STACK];
+ int seq_points[MAX_NUM_CARDS_IN_A_STACK];
+ int stacks_map[MAX_NUM_STACKS];
+ int junk_move_to_stacks[MAX_NUM_STACKS];
+ int num_separate_false_seqs;
+
+ fcs_card_t above_card, up_above_card;
+ int above_c;
+
+ int end_of_child_seq;
+ int child_num_true_seqs;
+
+ end_of_child_seq = cc;
+ child_num_true_seqs = 1;
+ while ((end_of_child_seq+1 < cards_num) &&
+ fcs_is_ss_false_parent(
+ fcs_stack_card(state, stack, end_of_child_seq),
+ fcs_stack_card(state, stack, end_of_child_seq+1)
+ )
+ )
+ {
+ child_num_true_seqs += (!fcs_is_ss_true_parent(
+ fcs_stack_card(state, stack, end_of_child_seq),
+ fcs_stack_card(state, stack, end_of_child_seq+1)
+ ));
+ end_of_child_seq++;
+ }
+
+ num_separate_false_seqs = 0;
+ above_card = fcs_stack_card(state, stack, cards_num-1);
+ above_num_true_seqs[num_separate_false_seqs] = 1;
+ for(above_c = cards_num-2;
+ above_c > end_of_child_seq ;
+ above_c--
+ )
+ {
+ up_above_card = fcs_stack_card(state, stack, above_c);
+ if (! fcs_is_ss_false_parent(up_above_card, above_card))
+ {
+ seq_points[num_separate_false_seqs++] = above_c+1;
+ above_num_true_seqs[num_separate_false_seqs] = 1;
+ }
+ above_num_true_seqs[num_separate_false_seqs] += ! (fcs_card_suit(up_above_card) == fcs_card_suit(above_card));
+ above_card = up_above_card;
+ }
+
+ if (end_of_child_seq < cards_num - 1)
+ {
+ seq_points[num_separate_false_seqs++] = above_c+1;
+ }
+
+ /* Add the child to the seq_points */
+ child_seq_index = num_separate_false_seqs;
+ above_num_true_seqs[num_separate_false_seqs] = child_num_true_seqs;
+ seq_points[num_separate_false_seqs++] = cc;
+
+ /* Add the cards between the parent and the child to the seq_points */
+
+ above_card = fcs_stack_card(state, stack, cc-1);
+ above_num_true_seqs[num_separate_false_seqs] = 1;
+ for(above_c = cc-2;
+ above_c > pc ;
+ above_c--
+ )
+ {
+ up_above_card = fcs_stack_card(state, stack, above_c);
+ if (! fcs_is_ss_false_parent(up_above_card, above_card))
+ {
+ seq_points[num_separate_false_seqs++] = above_c+1;
+ above_num_true_seqs[num_separate_false_seqs] = 1;
+ }
+ above_num_true_seqs[num_separate_false_seqs] += ! (fcs_card_suit(up_above_card) == fcs_card_suit(above_card));
+ above_card = up_above_card;
+ }
+
+ if (pc < cc - 1)
+ {
+ seq_points[num_separate_false_seqs++] = above_c+1;
+ }
+
+
+
+ for(a = 0 ; a < state_stacks_num ; a++)
+ {
+ stacks_map[a] = 0;
+ }
+ stacks_map[stack] = 1;
+
+ after_junk_num_freestacks = num_freestacks;
+
+ for(false_seq_index=0;false_seq_index<num_separate_false_seqs;false_seq_index++)
+ {
+ /* Find a suitable place to put it */
+ int clear_junk_dest_stack = -1;
+
+
+ /* Let's try to find a suitable parent on top one of the stacks */
+ for(clear_junk_dest_stack=0;
+ clear_junk_dest_stack < state_stacks_num;
+ clear_junk_dest_stack++
+ )
+ {
+ int clear_junk_stack_len;
+ clear_junk_stack_len = fcs_stack_len(state, clear_junk_dest_stack);
+
+ if ((clear_junk_stack_len > 0) && (stacks_map[clear_junk_dest_stack] == 0))
+ {
+ fcs_card_t clear_junk_dest_card;
+
+ clear_junk_dest_card = fcs_stack_card(state, clear_junk_dest_stack, clear_junk_stack_len-1);
+ if (fcs_is_ss_false_parent(clear_junk_dest_card, fcs_stack_card(state, stack, seq_points[false_seq_index])))
+ {
+ if (calc_max_sequence_move(0, after_junk_num_freestacks) >= above_num_true_seqs[false_seq_index])
+ {
+ stacks_map[clear_junk_dest_stack] = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ if (clear_junk_dest_stack == state_stacks_num)
+ {
+ clear_junk_dest_stack = -1;
+ }
+
+ if (clear_junk_dest_stack == -1)
+ {
+ /* Check if there is a vacant stack */
+ if (num_freestacks > 0)
+ {
+ if (calc_max_sequence_move(0, after_junk_num_freestacks-1) >= above_num_true_seqs[false_seq_index])
+ {
+ /* Find an empty stack and designate it as the destination for the junk */
+ for(
+ clear_junk_dest_stack = 0;
+ clear_junk_dest_stack < state_stacks_num;
+ clear_junk_dest_stack++
+ )
+ {
+ if ((fcs_stack_len(state, clear_junk_dest_stack) == 0) && (stacks_map[clear_junk_dest_stack] == 0))
+ {
+ stacks_map[clear_junk_dest_stack] = 1;
+ break;
+ }
+ }
+ }
+ after_junk_num_freestacks--;
+ }
+ }
+
+ if ((clear_junk_dest_stack == -1))
+ {
+ break;
+ }
+ junk_move_to_stacks[false_seq_index] = clear_junk_dest_stack;
+ }
+
+ if (false_seq_index == num_separate_false_seqs)
+ {
+ /* Let's check if we can move the child after we are done moving all the junk cards */
+ if (calc_max_sequence_move(0, after_junk_num_freestacks) >= child_num_true_seqs)
+ {
+ /* We can do it - so let's move everything */
+
+ sfs_check_state_begin();
+
+ /* Move the junk cards to their place */
+
+ my_copy_stack(stack);
+
+ for(false_seq_index=0;
+ false_seq_index<num_separate_false_seqs;
+ false_seq_index++
+ )
+ {
+ int start = seq_points[false_seq_index];
+ int end = ((false_seq_index == 0) ? (cards_num-1) : (seq_points[false_seq_index-1]-1));
+
+ my_copy_stack(junk_move_to_stacks[false_seq_index]);
+
+ fcs_move_sequence ( junk_move_to_stacks[false_seq_index], stack, start, end, a);
+ }
+
+ {
+ int end = fcs_stack_len(new_state, junk_move_to_stacks[child_seq_index])-1;
+ int start = end-(end_of_child_seq-cc);
+
+ my_copy_stack(junk_move_to_stacks[child_seq_index]);
+
+
+ fcs_move_sequence( stack, junk_move_to_stacks[child_seq_index], start, end, a);
+ }
+
+ sfs_check_state_end();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return FCS_STATE_IS_NOT_SOLVEABLE;
+}
+
+#undef state_with_locations
+#undef state
+#undef new_state_with_locations
+#undef new_state
+
diff --git a/kpat/freecell-solver/state.c b/kpat/freecell-solver/state.c
new file mode 100644
index 00000000..25453acb
--- /dev/null
+++ b/kpat/freecell-solver/state.c
@@ -0,0 +1,1114 @@
+/*
+ * state.c - state functions module for Freecell Solver
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "fcs_config.h"
+#include "state.h"
+#include "card.h"
+#include "fcs_enums.h"
+#include "app_str.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+
+#ifndef min
+#define min(a,b) ((a)<(b)?(a):(b))
+#endif
+
+
+#ifdef DEBUG_STATES
+
+fcs_card_t freecell_solver_empty_card = {0,0};
+
+#elif defined(COMPACT_STATES) || defined (INDIRECT_STACK_STATES)
+
+fcs_card_t freecell_solver_empty_card = (fcs_card_t)0;
+
+#endif
+
+static int fcs_card_compare(const void * card1, const void * card2)
+{
+ const fcs_card_t * c1 = (const fcs_card_t *)card1;
+ const fcs_card_t * c2 = (const fcs_card_t *)card2;
+
+ if (fcs_card_card_num(*c1) > fcs_card_card_num(*c2))
+ {
+ return 1;
+ }
+ else if (fcs_card_card_num(*c1) < fcs_card_card_num(*c2))
+ {
+ return -1;
+ }
+ else
+ {
+ if (fcs_card_suit(*c1) > fcs_card_suit(*c2))
+ {
+ return 1;
+ }
+ else if (fcs_card_suit(*c1) < fcs_card_suit(*c2))
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+}
+
+#ifdef DEBUG_STATES
+static int fcs_stack_compare(const void * s1, const void * s2)
+{
+ fcs_card_t card1 = ((const fc_stack_t *)s1)->cards[0];
+ fcs_card_t card2 = ((const fc_stack_t *)s2)->cards[0];
+
+ return fcs_card_compare(&card1, &card2);
+}
+#elif defined(COMPACT_STATES)
+static int fcs_stack_compare(const void * s1, const void * s2)
+{
+ fcs_card_t card1 = ((fcs_card_t*)s1)[1];
+ fcs_card_t card2 = ((fcs_card_t*)s2)[1];
+
+ return fcs_card_compare(&card1, &card2);
+}
+#elif defined(INDIRECT_STACK_STATES)
+
+
+#if MAX_NUM_DECKS == 1
+static int fcs_stack_compare_for_stack_sort(const void * s1, const void * s2)
+{
+ fcs_card_t card1 = ((fcs_card_t*)s1)[1];
+ fcs_card_t card2 = ((fcs_card_t*)s2)[1];
+
+ return fcs_card_compare(&card1, &card2);
+}
+#endif
+
+int freecell_solver_stack_compare_for_comparison(const void * v_s1, const void * v_s2)
+{
+ const fcs_card_t * s1 = (const fcs_card_t *)v_s1;
+ const fcs_card_t * s2 = (const fcs_card_t *)v_s2;
+
+ int min_len;
+ int a, ret;
+
+ min_len = min(s1[0], s2[0]);
+
+ for(a=0;a<min_len;a++)
+ {
+ ret = fcs_card_compare(s1+a+1,s2+a+1);
+ if (ret != 0)
+ {
+ return ret;
+ }
+ }
+ /*
+ * The reason I do the stack length comparisons after the card-by-card
+ * comparison is to maintain correspondence with
+ * fcs_stack_compare_for_stack_sort, and with the one card comparison
+ * of the other state representation mechanisms.
+ * */
+ if (s1[0] < s2[0])
+ {
+ return -1;
+ }
+ else if (s1[0] > s2[0])
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+#endif
+
+#ifdef FCS_WITH_TALONS
+static int fcs_talon_compare_with_context(const void * p1, const void * p2, fcs_compare_context_t context)
+{
+ fcs_card_t * t1 = (fcs_card_t *)p1;
+ fcs_card_t * t2 = (fcs_card_t *)p2;
+
+ if (t1[0] < t2[0])
+ {
+ return -1;
+ }
+ else if (t1[0] > t2[0])
+ {
+ return 1;
+ }
+ else
+ {
+ return memcmp(t1,t2,t1[0]+1);
+ }
+}
+#endif
+
+#ifdef DEBUG_STATES
+void freecell_solver_canonize_state(fcs_state_with_locations_t * state, int freecells_num, int stacks_num)
+{
+ int b,c;
+
+ fc_stack_t temp_stack;
+ fcs_card_t temp_freecell;
+ int temp_loc;
+
+ /* Insertion-sort the stacks */
+ for(b=1;b<stacks_num;b++)
+ {
+ c = b;
+ while(
+ (c>0) &&
+ (fcs_stack_compare(
+ &(state->s.stacks[c]),
+ &(state->s.stacks[c-1])
+ ) < 0)
+ )
+ {
+ temp_stack = state->s.stacks[c];
+ state->s.stacks[c] = state->s.stacks[c-1];
+ state->s.stacks[c-1] = temp_stack;
+
+ temp_loc = state->stack_locs[c];
+ state->stack_locs[c] = state->stack_locs[c-1];
+ state->stack_locs[c-1] = temp_loc;
+
+ c--;
+ }
+ }
+
+ /* Insertion sort the freecells */
+
+ for(b=1;b<freecells_num;b++)
+ {
+ c = b;
+ while(
+ (c>0) &&
+ (fcs_card_compare(
+ &(state->s.freecells[c]),
+ &(state->s.freecells[c-1])
+ ) < 0)
+ )
+ {
+ temp_freecell = state->s.freecells[c];
+ state->s.freecells[c] = state->s.freecells[c-1];
+ state->s.freecells[c-1] = temp_freecell;
+
+ temp_loc = state->fc_locs[c];
+ state->fc_locs[c] = state->fc_locs[c-1];
+ state->fc_locs[c-1] = temp_loc;
+
+ c--;
+ }
+ }
+}
+
+#elif defined(COMPACT_STATES)
+
+void freecell_solver_canonize_state(
+ fcs_state_with_locations_t * state,
+ int freecells_num,
+ int stacks_num)
+{
+ int b,c;
+
+ char temp_stack[(MAX_NUM_CARDS_IN_A_STACK+1)];
+ fcs_card_t temp_freecell;
+ char temp_loc;
+
+ /* Insertion-sort the stacks */
+
+ for(b=1;b<stacks_num;b++)
+ {
+ c = b;
+ while(
+ (c>0) &&
+ (fcs_stack_compare(
+ state->s.data+c*(MAX_NUM_CARDS_IN_A_STACK+1),
+ state->s.data+(c-1)*(MAX_NUM_CARDS_IN_A_STACK+1)
+ ) < 0)
+ )
+ {
+ memcpy(temp_stack, state->s.data+c*(MAX_NUM_CARDS_IN_A_STACK+1), (MAX_NUM_CARDS_IN_A_STACK+1));
+ memcpy(state->s.data+c*(MAX_NUM_CARDS_IN_A_STACK+1), state->s.data+(c-1)*(MAX_NUM_CARDS_IN_A_STACK+1), (MAX_NUM_CARDS_IN_A_STACK+1));
+ memcpy(state->s.data+(c-1)*(MAX_NUM_CARDS_IN_A_STACK+1), temp_stack, (MAX_NUM_CARDS_IN_A_STACK+1));
+
+ temp_loc = state->stack_locs[c];
+ state->stack_locs[c] = state->stack_locs[c-1];
+ state->stack_locs[c-1] = temp_loc;
+
+ c--;
+ }
+ }
+
+ /* Insertion-sort the freecells */
+
+ for(b=1;b<freecells_num;b++)
+ {
+ c = b;
+
+ while(
+ (c>0) &&
+ (fcs_card_compare(
+ state->s.data+FCS_FREECELLS_OFFSET+c,
+ state->s.data+FCS_FREECELLS_OFFSET+c-1
+ ) < 0)
+ )
+ {
+ temp_freecell = (state->s.data[FCS_FREECELLS_OFFSET+c]);
+ state->s.data[FCS_FREECELLS_OFFSET+c] = state->s.data[FCS_FREECELLS_OFFSET+c-1];
+ state->s.data[FCS_FREECELLS_OFFSET+c-1] = temp_freecell;
+
+ temp_loc = state->fc_locs[c];
+ state->fc_locs[c] = state->fc_locs[c-1];
+ state->fc_locs[c-1] = temp_loc;
+
+ c--;
+ }
+ }
+}
+#elif defined(INDIRECT_STACK_STATES)
+void freecell_solver_canonize_state(
+ fcs_state_with_locations_t * state,
+ int freecells_num,
+ int stacks_num)
+{
+ int b,c;
+ fcs_card_t * temp_stack;
+ fcs_card_t temp_freecell;
+ char temp_loc;
+
+ /* Insertion-sort the stacks */
+ for(b=1;b<stacks_num;b++)
+ {
+ c = b;
+ while(
+ (c>0) &&
+ (
+#if MAX_NUM_DECKS > 1
+ freecell_solver_stack_compare_for_comparison
+#else
+ fcs_stack_compare_for_stack_sort
+#endif
+ (
+ (state->s.stacks[c]),
+ (state->s.stacks[c-1])
+ )
+ < 0
+ )
+ )
+ {
+ temp_stack = state->s.stacks[c];
+ state->s.stacks[c] = state->s.stacks[c-1];
+ state->s.stacks[c-1] = temp_stack;
+
+ temp_loc = state->stack_locs[c];
+ state->stack_locs[c] = state->stack_locs[c-1];
+ state->stack_locs[c-1] = temp_loc;
+
+ c--;
+ }
+ }
+
+ /* Insertion sort the freecells */
+
+ for(b=1;b<freecells_num;b++)
+ {
+ c = b;
+ while(
+ (c>0) &&
+ (fcs_card_compare(
+ &(state->s.freecells[c]),
+ &(state->s.freecells[c-1])
+ ) < 0)
+ )
+ {
+ temp_freecell = state->s.freecells[c];
+ state->s.freecells[c] = state->s.freecells[c-1];
+ state->s.freecells[c-1] = temp_freecell;
+
+ temp_loc = state->fc_locs[c];
+ state->fc_locs[c] = state->fc_locs[c-1];
+ state->fc_locs[c-1] = temp_loc;
+
+ c--;
+ }
+ }
+}
+
+#endif
+
+static void fcs_state_init(
+ fcs_state_with_locations_t * state,
+ int stacks_num
+#ifdef INDIRECT_STACK_STATES
+ ,fcs_card_t * indirect_stacks_buffer
+#endif
+ )
+{
+ int a;
+ memset((void*)&(state->s), 0, sizeof(fcs_state_t));
+ for(a=0;a<MAX_NUM_STACKS;a++)
+ {
+ state->stack_locs[a] = a;
+ }
+#ifdef INDIRECT_STACK_STATES
+ for(a=0;a<stacks_num;a++)
+ {
+ state->s.stacks[a] = &indirect_stacks_buffer[a << 7];
+ memset(state->s.stacks[a], '\0', MAX_NUM_DECKS*52+1);
+ }
+ for(;a<MAX_NUM_STACKS;a++)
+ {
+ state->s.stacks[a] = NULL;
+ }
+#endif
+ for(a=0;a<MAX_NUM_FREECELLS;a++)
+ {
+ state->fc_locs[a] = a;
+ }
+}
+
+
+#if (FCS_STATE_STORAGE != FCS_STATE_STORAGE_INDIRECT)
+int freecell_solver_state_compare(const void * s1, const void * s2)
+{
+ return memcmp(s1,s2,sizeof(fcs_state_t));
+}
+
+int freecell_solver_state_compare_equal(const void * s1, const void * s2)
+{
+ return (!memcmp(s1,s2,sizeof(fcs_state_t)));
+}
+
+
+int freecell_solver_state_compare_with_context(
+ const void * s1,
+ const void * s2,
+ fcs_compare_context_t context
+ )
+{
+ (void)context;
+ return memcmp(s1,s2,sizeof(fcs_state_t));
+}
+#else
+int freecell_solver_state_compare_indirect(const void * s1, const void * s2)
+{
+ return memcmp(*(fcs_state_with_locations_t * *)s1, *(fcs_state_with_locations_t * *)s2, sizeof(fcs_state_t));
+}
+
+int freecell_solver_state_compare_indirect_with_context(const void * s1, const void * s2, void * context)
+{
+ return memcmp(*(fcs_state_with_locations_t * *)s1, *(fcs_state_with_locations_t * *)s2, sizeof(fcs_state_t));
+}
+#endif
+
+static const char * const freecells_prefixes[] = { "FC:", "Freecells:", "Freecell:", ""};
+static const char * const foundations_prefixes[] = { "Decks:", "Deck:", "Founds:", "Foundations:", "Foundation:", "Found:", ""};
+static const char * const talon_prefixes[] = { "Talon:", "Queue:" , ""};
+static const char * const num_redeals_prefixes[] = { "Num-Redeals:", "Readels-Num:", "Readeals-Number:", ""};
+
+#ifdef WIN32
+#define strncasecmp(a,b,c) (strnicmp((a),(b),(c)))
+#endif
+
+int freecell_solver_initial_user_state_to_c(
+ const char * string,
+ fcs_state_with_locations_t * out_state,
+ int freecells_num,
+ int stacks_num,
+ int decks_num
+#ifdef FCS_WITH_TALONS
+ ,int talon_type
+#endif
+#ifdef INDIRECT_STACK_STATES
+ , fcs_card_t * indirect_stacks_buffer
+#endif
+ )
+{
+ fcs_state_with_locations_t ret_with_locations;
+
+ int s,c;
+ const char * str;
+ fcs_card_t card;
+ int first_line;
+
+ int prefix_found;
+ const char * const * prefixes;
+ int i;
+ int decks_index[4];
+
+ fcs_state_init(
+ &ret_with_locations,
+ stacks_num
+#ifdef INDIRECT_STACK_STATES
+ , indirect_stacks_buffer
+#endif
+ );
+ str = string;
+
+ first_line = 1;
+
+#define ret (ret_with_locations.s)
+/* Handle the end of string - shouldn't happen */
+#define handle_eos() \
+ { \
+ if ((*str) == '\0') \
+ { \
+ return FCS_USER_STATE_TO_C__PREMATURE_END_OF_INPUT; \
+ } \
+ }
+
+#ifdef FCS_WITH_TALONS
+ if (talon_type == FCS_TALON_KLONDIKE)
+ {
+ fcs_klondike_talon_num_redeals_left(ret) = -1;
+ }
+#endif
+
+ for(s=0;s<stacks_num;s++)
+ {
+ /* Move to the next stack */
+ if (!first_line)
+ {
+ while((*str) != '\n')
+ {
+ handle_eos();
+ str++;
+ }
+ str++;
+ }
+ first_line = 0;
+
+ prefixes = freecells_prefixes;
+ prefix_found = 0;
+ for(i=0;prefixes[i][0] != '\0'; i++)
+ {
+ if (!strncasecmp(str, prefixes[i], strlen(prefixes[i])))
+ {
+ prefix_found = 1;
+ str += strlen(prefixes[i]);
+ break;
+ }
+ }
+
+ if (prefix_found)
+ {
+ for(c=0;c<freecells_num;c++)
+ {
+ fcs_empty_freecell(ret, c);
+ }
+ for(c=0;c<freecells_num;c++)
+ {
+ if (c!=0)
+ {
+ while(
+ ((*str) != ' ') &&
+ ((*str) != '\t') &&
+ ((*str) != '\n') &&
+ ((*str) != '\r')
+ )
+ {
+ handle_eos();
+ str++;
+ }
+ if ((*str == '\n') || (*str == '\r'))
+ {
+ break;
+ }
+ str++;
+ }
+
+ while ((*str == ' ') || (*str == '\t'))
+ {
+ str++;
+ }
+ if ((*str == '\r') || (*str == '\n'))
+ break;
+
+ if ((*str == '*') || (*str == '-'))
+ {
+ card = fcs_empty_card;
+ }
+ else
+ {
+ card = fcs_card_user2perl(str);
+ }
+
+ fcs_put_card_in_freecell(ret, c, card);
+ }
+
+ while (*str != '\n')
+ {
+ handle_eos();
+ str++;
+ }
+ s--;
+ continue;
+ }
+
+ prefixes = foundations_prefixes;
+ prefix_found = 0;
+ for(i=0;prefixes[i][0] != '\0'; i++)
+ {
+ if (!strncasecmp(str, prefixes[i], strlen(prefixes[i])))
+ {
+ prefix_found = 1;
+ str += strlen(prefixes[i]);
+ break;
+ }
+ }
+
+ if (prefix_found)
+ {
+ int d;
+
+ for(d=0;d<decks_num*4;d++)
+ {
+ fcs_set_foundation(ret, d, 0);
+ }
+
+ for(d=0;d<4;d++)
+ {
+ decks_index[d] = 0;
+ }
+ while (1)
+ {
+ while((*str == ' ') || (*str == '\t'))
+ str++;
+ if ((*str == '\n') || (*str == '\r'))
+ break;
+ d = fcs_u2p_suit(str);
+ str++;
+ while (*str == '-')
+ str++;
+ c = fcs_u2p_card_number(str);
+ while (
+ (*str != ' ') &&
+ (*str != '\t') &&
+ (*str != '\n') &&
+ (*str != '\r')
+ )
+ {
+ handle_eos();
+ str++;
+ }
+
+ fcs_set_foundation(ret, (decks_index[d]*4+d), c);
+ decks_index[d]++;
+ if (decks_index[d] >= decks_num)
+ {
+ decks_index[d] = 0;
+ }
+ }
+ s--;
+ continue;
+ }
+
+#ifdef FCS_WITH_TALONS
+ prefixes = talon_prefixes;
+ prefix_found = 0;
+ for(i=0;prefixes[i][0] != '\0'; i++)
+ {
+ if (!strncasecmp(str, prefixes[i], strlen(prefixes[i])))
+ {
+ prefix_found = 1;
+ str += strlen(prefixes[i]);
+ break;
+ }
+ }
+
+ if (prefix_found)
+ {
+ /* Input the Talon */
+ int talon_size;
+
+ talon_size = MAX_NUM_DECKS*52+16;
+ ret.talon = malloc(sizeof(fcs_card_t)*talon_size);
+ fcs_talon_pos(ret) = 0;
+
+ for(c=0 ; c < talon_size ; c++)
+ {
+ /* Move to the next card */
+ if (c!=0)
+ {
+ while(
+ ((*str) != ' ') &&
+ ((*str) != '\t') &&
+ ((*str) != '\n') &&
+ ((*str) != '\r')
+ )
+ {
+ handle_eos();
+ str++;
+ }
+ if ((*str == '\n') || (*str == '\r'))
+ {
+ break;
+ }
+ }
+
+ while ((*str == ' ') || (*str == '\t'))
+ {
+ str++;
+ }
+
+ if ((*str == '\n') || (*str == '\r'))
+ {
+ break;
+ }
+
+ card = fcs_card_user2perl(str);
+
+ fcs_put_card_in_talon(ret, c+(talon_type==FCS_TALON_KLONDIKE), card);
+ }
+ fcs_talon_len(ret) = c;
+
+ if (talon_type == FCS_TALON_KLONDIKE)
+ {
+ int talon_len;
+
+ talon_len = fcs_talon_len(ret);
+ fcs_klondike_talon_len(ret) = talon_len;
+ fcs_klondike_talon_stack_pos(ret) = -1;
+ fcs_klondike_talon_queue_pos(ret) = 0;
+ }
+
+ s--;
+ continue;
+ }
+
+ prefixes = num_redeals_prefixes;
+ prefix_found = 0;
+ for(i=0;prefixes[i][0] != '\0'; i++)
+ {
+ if (!strncasecmp(str, prefixes[i], strlen(prefixes[i])))
+ {
+ prefix_found = 1;
+ str += strlen(prefixes[i]);
+ break;
+ }
+ }
+
+ if (prefix_found)
+ {
+ while ((*str < '0') && (*str > '9') && (*str != '\n'))
+ {
+ handle_eos();
+ str++;
+ }
+ if (*str != '\n')
+ {
+ int num_redeals;
+
+ num_redeals = atoi(str);
+ if (talon_type == FCS_TALON_KLONDIKE)
+ {
+ fcs_klondike_talon_num_redeals_left(ret) =
+ (num_redeals < 0) ?
+ (-1) :
+ ((num_redeals > 127) ? 127 : num_redeals)
+ ;
+ }
+ }
+ s--;
+ continue;
+ }
+#endif
+
+ for(c=0 ; c < MAX_NUM_CARDS_IN_A_STACK ; c++)
+ {
+ /* Move to the next card */
+ if (c!=0)
+ {
+ while(
+ ((*str) != ' ') &&
+ ((*str) != '\t') &&
+ ((*str) != '\n') &&
+ ((*str) != '\r')
+ )
+ {
+ handle_eos();
+ str++;
+ }
+ if ((*str == '\n') || (*str == '\r'))
+ {
+ break;
+ }
+ }
+
+ while ((*str == ' ') || (*str == '\t'))
+ {
+ str++;
+ }
+ if ((*str == '\n') || (*str == '\r'))
+ {
+ break;
+ }
+ card = fcs_card_user2perl(str);
+
+ fcs_push_card_into_stack(ret, s, card);
+ }
+ }
+
+ *out_state = ret_with_locations;
+ return FCS_USER_STATE_TO_C__SUCCESS;
+}
+
+#undef ret
+#undef handle_eos
+
+int freecell_solver_check_state_validity(
+ fcs_state_with_locations_t * state_with_locations,
+ int freecells_num,
+ int stacks_num,
+ int decks_num,
+#ifdef FCS_WITH_TALONS
+ int talon_type,
+#endif
+ fcs_card_t * misplaced_card)
+{
+ int cards[4][14];
+ int c, s, d, f;
+
+ fcs_state_t * state;
+
+ state = (&(state_with_locations->s));
+
+ /* Initialize all cards to 0 */
+ for(d=0;d<4;d++)
+ {
+ for(c=1;c<=13;c++)
+ {
+ cards[d][c] = 0;
+ }
+ }
+
+ /* Mark the cards in the decks */
+ for(d=0;d<decks_num*4;d++)
+ {
+ for(c=1;c<=fcs_foundation_value(*state, d);c++)
+ {
+ cards[d%4][c]++;
+ }
+ }
+
+ /* Mark the cards in the freecells */
+ for(f=0;f<freecells_num;f++)
+ {
+ if (fcs_freecell_card_num(*state, f) != 0)
+ {
+ cards
+ [fcs_freecell_card_suit(*state, f)]
+ [fcs_freecell_card_num(*state, f)] ++;
+ }
+ }
+
+ /* Mark the cards in the stacks */
+ for(s=0;s<stacks_num;s++)
+ {
+ for(c=0;c<fcs_stack_len(*state,s);c++)
+ {
+ if (fcs_stack_card_num(*state, s, c) == 0)
+ {
+ *misplaced_card = fcs_empty_card;
+ return 3;
+ }
+ cards
+ [fcs_stack_card_suit(*state, s, c)]
+ [fcs_stack_card_num(*state, s, c)] ++;
+
+ }
+ }
+
+#ifdef FCS_WITH_TALONS
+ /* Mark the cards in the (gypsy) talon */
+ if ((talon_type == FCS_TALON_GYPSY) || (talon_type == FCS_TALON_KLONDIKE))
+ {
+ for(c = ((talon_type == FCS_TALON_GYPSY)?fcs_talon_pos(*state):1) ;
+ c < ((talon_type==FCS_TALON_GYPSY) ? fcs_talon_len(*state) : (fcs_klondike_talon_len(*state)+1)) ;
+ c++)
+ {
+ if (fcs_get_talon_card(*state,c) != fcs_empty_card)
+ {
+ cards
+ [fcs_card_suit(fcs_get_talon_card(*state, c))]
+ [fcs_card_card_num(fcs_get_talon_card(*state, c))] ++;
+ }
+ }
+ }
+#endif
+
+ /* Now check if there are extra or missing cards */
+
+ for(d=0;d<4;d++)
+ {
+ for(c=1;c<=13;c++)
+ {
+ if (cards[d][c] != decks_num)
+ {
+ *misplaced_card = fcs_empty_card;
+ fcs_card_set_suit(*misplaced_card, d);
+ fcs_card_set_num(*misplaced_card, c);
+ return (cards[d][c] < decks_num) ? 1 : 2;
+ }
+ }
+ }
+
+ return 0;
+}
+
+#undef state
+
+
+char * freecell_solver_state_as_string(
+ fcs_state_with_locations_t * state_with_locations,
+ int freecells_num,
+ int stacks_num,
+ int decks_num,
+ int parseable_output,
+ int canonized_order_output,
+ int display_10_as_t
+ )
+{
+ fcs_state_t * state;
+ char freecell[10], decks[MAX_NUM_DECKS*4][10], stack_card_[10];
+ int a, card_num_is_null, b;
+ int max_num_cards, s, card_num, len;
+
+ char str2[128], str3[128], * str2_ptr, * str3_ptr;
+
+ freecell_solver_append_string_t * app_str;
+
+ int stack_locs[MAX_NUM_STACKS];
+ int freecell_locs[MAX_NUM_FREECELLS];
+
+ state = (&(state_with_locations->s));
+
+ if (canonized_order_output)
+ {
+ for(a=0;a<stacks_num;a++)
+ {
+ stack_locs[a] = a;
+ }
+ for(a=0;a<freecells_num;a++)
+ {
+ freecell_locs[a] = a;
+ }
+ }
+ else
+ {
+ for(a=0;a<stacks_num;a++)
+ {
+ stack_locs[(int)(state_with_locations->stack_locs[a])] = a;
+ }
+ for(a=0;a<freecells_num;a++)
+ {
+ freecell_locs[(int)(state_with_locations->fc_locs[a])] = a;
+ }
+ }
+
+ for(a=0;a<decks_num*4;a++)
+ {
+ fcs_p2u_card_number(
+ fcs_foundation_value(*state, a),
+ decks[a],
+ &card_num_is_null,
+ display_10_as_t,
+ 0
+ );
+ if (decks[a][0] == ' ')
+ decks[a][0] = '0';
+ }
+
+ app_str = freecell_solver_append_string_alloc(512);
+
+ if(!parseable_output)
+ {
+ for(a=0;a<((freecells_num/4)+((freecells_num%4==0)?0:1));a++)
+ {
+ str2_ptr = str2;
+ str3_ptr = str3;
+ for(b=0;b<min(freecells_num-a*4, 4);b++)
+ {
+ str2_ptr += sprintf(str2_ptr, "%3s ",
+ fcs_card_perl2user(
+ fcs_freecell_card(
+ *state,
+ freecell_locs[a*4+b]
+ ),
+ freecell,
+ display_10_as_t
+ )
+ );
+ str3_ptr += sprintf(str3_ptr, "--- ");
+ }
+ if (a < decks_num)
+ {
+ freecell_solver_append_string_sprintf(
+ app_str,
+ "%-16s H-%1s C-%1s D-%1s S-%1s\n",
+ str2,
+ decks[a*4],
+ decks[a*4+1],
+ decks[a*4+2],
+ decks[a*4+3]
+ );
+ }
+ else
+ {
+ freecell_solver_append_string_sprintf(
+ app_str,
+ "%s\n", str2
+ );
+ }
+ freecell_solver_append_string_sprintf(
+ app_str,
+ "%s\n", str3
+ );
+ }
+ for(;a<decks_num;a++)
+ {
+ freecell_solver_append_string_sprintf(
+ app_str,
+ "%-16s H-%1s C-%1s D-%1s S-%1s\n",
+ "",
+ decks[a*4],
+ decks[a*4+1],
+ decks[a*4+2],
+ decks[a*4+3]
+ );
+ }
+ freecell_solver_append_string_sprintf(
+ app_str,
+ "%s",
+ "\n\n"
+ );
+
+ for(s=0;s<stacks_num;s++)
+ {
+ freecell_solver_append_string_sprintf(app_str, "%s", " -- ");
+ }
+ freecell_solver_append_string_sprintf(
+ app_str,
+ "%s",
+ "\n"
+ );
+
+ max_num_cards = 0;
+ for(s=0;s<stacks_num;s++)
+ {
+ if (fcs_stack_len(*state, stack_locs[s]) > max_num_cards)
+ {
+ max_num_cards = fcs_stack_len(*state, stack_locs[s]);
+ }
+ }
+
+ for(card_num=0;card_num<max_num_cards;card_num++)
+ {
+ for(s = 0; s<stacks_num; s++)
+ {
+ if (card_num >= fcs_stack_len(*state, stack_locs[s]))
+ {
+ freecell_solver_append_string_sprintf(
+ app_str,
+ " "
+ );
+ }
+ else
+ {
+ freecell_solver_append_string_sprintf(
+ app_str,
+ "%3s ",
+ fcs_card_perl2user(
+ fcs_stack_card(
+ *state,
+ stack_locs[s],
+ card_num),
+ stack_card_,
+ display_10_as_t
+ )
+ );
+ }
+ }
+ freecell_solver_append_string_sprintf(app_str, "%s", "\n");
+ }
+ }
+ else
+ {
+ freecell_solver_append_string_sprintf(app_str, "%s", "Foundations: ");
+ for(a=0;a<decks_num;a++)
+ {
+ freecell_solver_append_string_sprintf(
+ app_str,
+ "H-%s C-%s D-%s S-%s ",
+ decks[a*4],
+ decks[a*4+1],
+ decks[a*4+2],
+ decks[a*4+3]
+ );
+ }
+
+ freecell_solver_append_string_sprintf(app_str, "%s", "\nFreecells: ");
+
+ for(a=0;a<freecells_num;a++)
+ {
+ freecell_solver_append_string_sprintf(
+ app_str,
+ "%3s",
+ fcs_card_perl2user(
+ fcs_freecell_card(
+ *state,
+ freecell_locs[a]
+ ),
+ freecell,
+ display_10_as_t
+ )
+ );
+ if (a < freecells_num-1)
+ {
+ freecell_solver_append_string_sprintf(app_str, "%s", " ");
+ }
+ }
+ freecell_solver_append_string_sprintf(app_str, "%s", "\n");
+
+ for(s=0;s<stacks_num;s++)
+ {
+ freecell_solver_append_string_sprintf(app_str, "%s", ": ");
+
+ len = fcs_stack_len(*state, stack_locs[s]);
+ for(card_num=0;card_num<len;card_num++)
+ {
+ fcs_card_perl2user(
+ fcs_stack_card(
+ *state,
+ stack_locs[s],
+ card_num
+ ),
+ stack_card_,
+ display_10_as_t
+ );
+ freecell_solver_append_string_sprintf(app_str, "%s", stack_card_);
+ if (card_num < len-1)
+ {
+ freecell_solver_append_string_sprintf(app_str, "%s", " ");
+ }
+ }
+ freecell_solver_append_string_sprintf(app_str, "%s", "\n");
+ }
+ }
+
+ return freecell_solver_append_string_finalize(app_str);
+}
diff --git a/kpat/freecell-solver/state.h b/kpat/freecell-solver/state.h
new file mode 100644
index 00000000..e52b717c
--- /dev/null
+++ b/kpat/freecell-solver/state.h
@@ -0,0 +1,660 @@
+/*
+ * state.h - header file for state functions and macros for Freecell Solver
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+#include "fcs_config.h"
+
+#include "fcs_move.h"
+
+#ifndef FC_SOLVE__STATE_H
+#define FC_SOLVE__STATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if MAX_NUM_INITIAL_CARDS_IN_A_STACK+12>(MAX_NUM_DECKS*52)
+#define MAX_NUM_CARDS_IN_A_STACK (MAX_NUM_DECKS*52)
+#else
+#define MAX_NUM_CARDS_IN_A_STACK (MAX_NUM_INITIAL_CARDS_IN_A_STACK+12)
+#endif
+
+#define MAX_NUM_SCANS_BUCKETS 1
+#define MAX_NUM_SCANS (MAX_NUM_SCANS_BUCKETS * (sizeof(int)*8))
+
+/**********
+ * TODO: Change 5 to the log2 of sizeof(int)*8
+ *
+ ************/
+
+#define is_scan_visited(ptr_state, scan_id) (ptr_state->scan_visited[(scan_id)>>5] & (1 << ((scan_id)&((1<<(5))-1))))
+#define set_scan_visited(ptr_state, scan_id) { ptr_state->scan_visited[(scan_id)>>5] |= (1 << ((scan_id)&((1<<(5))-1))); }
+
+
+#ifdef DEBUG_STATES
+
+struct fcs_struct_card_t
+{
+ short card_num;
+ char suit;
+ char flags;
+};
+
+typedef struct fcs_struct_card_t fcs_card_t;
+
+struct fcs_struct_stack_t
+{
+ unsigned int num_cards;
+ fcs_card_t cards[MAX_NUM_CARDS_IN_A_STACK];
+};
+
+typedef struct fcs_struct_stack_t fc_stack_t;
+
+struct fcs_struct_state_t
+{
+ fc_stack_t stacks[MAX_NUM_STACKS];
+ fcs_card_t freecells[MAX_NUM_FREECELLS];
+ int foundations[MAX_NUM_DECKS*4];
+#ifdef FCS_WITH_TALONS
+ fcs_card_t * talon;
+ char talon_params[4];
+#endif
+};
+
+typedef struct fcs_struct_state_t fcs_state_t;
+
+#if 0
+struct fcs_struct_state_with_locations_t
+{
+ fcs_state_t s;
+ int stack_locs[MAX_NUM_STACKS];
+ int fc_locs[MAX_NUM_FREECELLS];
+ struct fcs_struct_state_with_locations_t * parent;
+ fcs_move_stack_t * moves_to_parent;
+ int depth;
+ int visited;
+ int visited_iter;
+ int num_active_children;
+ int scan_visited[MAX_NUM_SCANS_BUCKETS];
+};
+
+typedef struct fcs_struct_state_with_locations_t fcs_state_with_locations_t;
+#endif
+typedef int fcs_locs_t;
+
+#define fcs_stack_len(state, s) \
+ ( (state).stacks[(s)].num_cards )
+
+#define fcs_stack_card(state, s, c) \
+ ( (state).stacks[(s)].cards[(c)] )
+
+#define fcs_stack_card_suit(state, s, c) \
+ ( fcs_card_suit(fcs_stack_card((state),(s),(c))) )
+
+#define fcs_stack_card_num(state, s, c) \
+ ( fcs_card_card_num(fcs_stack_card((state),(s),(c))) )
+
+#define fcs_card_card_num(card) \
+ ( (card).card_num )
+
+#define fcs_card_suit(card) \
+ ((int)( (card).suit ))
+
+#define fcs_card_get_flipped(card) \
+ ( (card).flags )
+
+#define fcs_freecell_card(state, f) \
+ ( (state).freecells[(f)] )
+
+#define fcs_freecell_card_num(state, f) \
+ ( fcs_card_card_num(fcs_freecell_card((state),(f))) )
+
+#define fcs_freecell_card_suit(state, f) \
+ ( fcs_card_suit(fcs_freecell_card((state),(f))) )
+
+#define fcs_foundation_value(state, found) \
+ ( (state).foundations[(found)] )
+
+#define fcs_increment_foundation(state, found) \
+ ( (state).foundations[(found)]++ )
+
+#define fcs_set_foundation(state, found, value) \
+ ( (state).foundations[(found)] = (value) )
+
+#define fcs_pop_stack_card(state, s, into) \
+ { \
+ into = (state).stacks[(s)].cards[(state).stacks[(s)].num_cards-1]; \
+ (state).stacks[(s)].cards[(state).stacks[(s)].num_cards-1] = fcs_empty_card; \
+ (state).stacks[(s)].num_cards--; \
+ }
+
+#define fcs_push_stack_card_into_stack(state, ds, ss, sc) \
+ { \
+ (state).stacks[(ds)].cards[(state).stacks[(ds)].num_cards] = (state).stacks[(ss)].cards[(sc)]; \
+ (state).stacks[(ds)].num_cards++; \
+ }
+
+#define fcs_push_card_into_stack(state, ds, from) \
+ { \
+ (state).stacks[(ds)].cards[(state).stacks[(ds)].num_cards] = (from); \
+ (state).stacks[(ds)].num_cards++; \
+ }
+
+#define fcs_duplicate_state(dest, src) \
+ (dest) = (src)
+
+#define fcs_put_card_in_freecell(state, f, card) \
+ (state).freecells[(f)] = (card)
+
+#define fcs_empty_freecell(state, f) \
+ (state).freecells[(f)] = fcs_empty_card
+
+#define fcs_card_set_suit(card, d) \
+ (card).suit = (d)
+
+#define fcs_card_set_num(card, num) \
+ (card).card_num = (num)
+
+#define fcs_card_set_flipped(card, flipped) \
+ (card).flags = (flipped)
+
+#define fcs_flip_stack_card(state, s, c) \
+ fcs_card_set_flipped(fcs_stack_card((state),(s),(c)), 0)
+
+#ifdef FCS_WITH_TALONS
+#define fcs_talon_len(state) \
+ ((state).talon_params[0])
+
+#define fcs_talon_pos(state) \
+ ((state).talon_params[1])
+
+#define fcs_get_talon_card(state, pos) \
+ ((state).talon[pos])
+
+#define fcs_put_card_in_talon(state, pos, card) \
+ ((state).talon[pos] = (card))
+#endif
+
+#define fcs_copy_stack(state, idx, buffer) {}
+
+#elif defined(COMPACT_STATES) /* #ifdef DEBUG_STATES */
+
+
+
+
+
+
+
+typedef char fcs_card_t;
+/*
+ * Card:
+ * Bits 0-3 - Card Number
+ * Bits 4-5 - Deck
+ *
+ */
+
+struct fcs_struct_state_t
+{
+ char data[MAX_NUM_STACKS*(MAX_NUM_CARDS_IN_A_STACK+1)+MAX_NUM_FREECELLS+4*MAX_NUM_DECKS];
+#ifdef FCS_WITH_TALON
+ fcs_card_t * talon;
+ char talon_params[4];
+#endif
+};
+/*
+ * Stack: 0 - Number of cards
+ * 1-19 - Cards
+ * Stacks: stack_num*20 where stack_num >= 0 and
+ * stack_num <= (MAX_NUM_STACKS-1)
+ * Bytes: (MAX_NUM_STACKS*20) to
+ * (MAX_NUM_STACKS*20+MAX_NUM_FREECELLS-1)
+ * are Freecells.
+ * Bytes: (MAX_NUM_STACKS*20+MAX_NUM_FREECELLS) to
+ * MAX_NUM_STACKS*20+MAX_NUM_FREECELLS+3
+ * are Foundations.
+ * */
+
+/* ===== Depracated Information =====
+ * Stack: 0 - Number of cards 1-19 - Cards
+ * Stacks: stack_num*20 where stack_num >= 0 and stack_num <= 7
+ * Bytes 160-163 - Freecells
+ * Bytes 164-167 - Decks
+ */
+
+typedef struct fcs_struct_state_t fcs_state_t;
+
+#if 0
+struct fcs_struct_state_with_locations_t
+{
+ fcs_state_t s;
+ char stack_locs[MAX_NUM_STACKS];
+ char fc_locs[MAX_NUM_FREECELLS];
+ struct fcs_struct_state_with_locations_t * parent;
+ fcs_move_stack_t * moves_to_parent;
+ int depth;
+ int visited;
+ int visited_iter;
+ int num_active_children;
+ int scan_visited[MAX_NUM_SCANS_BUCKETS];
+};
+
+typedef struct fcs_struct_state_with_locations_t fcs_state_with_locations_t;
+#endif
+typedef char fcs_locs_t;
+
+#define fcs_card_card_num(card) \
+ ( (card) & 0x0F )
+
+#define fcs_card_suit(card) \
+ ( ((card) >> 4) & 0x03 )
+
+#define fcs_stack_len(state, s) \
+ ( (size_t)(state).data[s*(MAX_NUM_CARDS_IN_A_STACK+1)] )
+
+#define fcs_stack_card(state, s, c) \
+ ( (state).data[(s)*(MAX_NUM_CARDS_IN_A_STACK+1)+(c)+1] )
+
+#define fcs_stack_card_num(state, s, c) \
+ ( fcs_card_card_num(fcs_stack_card((state),(s),(c))) )
+
+#define fcs_stack_card_suit(state, s, c) \
+ ( fcs_card_suit(fcs_stack_card((state),(s),(c))) )
+
+#define FCS_FREECELLS_OFFSET ((MAX_NUM_STACKS)*(MAX_NUM_CARDS_IN_A_STACK+1))
+
+#define fcs_freecell_card(state, f) \
+ ( (state).data[FCS_FREECELLS_OFFSET+(f)] )
+
+#define fcs_freecell_card_num(state, f) \
+ ( fcs_card_card_num(fcs_freecell_card((state),(f))) )
+
+#define fcs_freecell_card_suit(state, f) \
+ ( fcs_card_suit(fcs_freecell_card((state),(f))) )
+
+#define FCS_FOUNDATIONS_OFFSET (((MAX_NUM_STACKS)*(MAX_NUM_CARDS_IN_A_STACK+1))+(MAX_NUM_FREECELLS))
+
+#define fcs_foundation_value(state, d) \
+ ( (state).data[FCS_FOUNDATIONS_OFFSET+(d)])
+
+#define fcs_increment_foundation(state, d) \
+ ( (state).data[FCS_FOUNDATIONS_OFFSET+(d)]++ )
+
+#define fcs_set_foundation(state, d, value) \
+ ( (state).data[FCS_FOUNDATIONS_OFFSET+(d)] = (value) )
+
+#define fcs_pop_stack_card(state, s, into) \
+ { \
+ into = fcs_stack_card((state), (s), (fcs_stack_len((state), (s))-1)); \
+ (state).data[((s)*(MAX_NUM_CARDS_IN_A_STACK+1))+1+(fcs_stack_len((state), (s))-1)] = fcs_empty_card; \
+ (state).data[(s)*(MAX_NUM_CARDS_IN_A_STACK+1)]--; \
+ }
+
+#define fcs_push_card_into_stack(state, ds, from) \
+ { \
+ (state).data[(ds)*(MAX_NUM_CARDS_IN_A_STACK+1)+1+fcs_stack_len((state), (ds))] = (from); \
+ (state).data[(ds)*(MAX_NUM_CARDS_IN_A_STACK+1)]++; \
+ }
+
+#define fcs_push_stack_card_into_stack(state, ds, ss, sc) \
+ fcs_push_card_into_stack((state), (ds), fcs_stack_card((state), (ss), (sc)))
+
+#define fcs_duplicate_state(dest, src) \
+ (dest) = (src)
+
+#define fcs_put_card_in_freecell(state, f, card) \
+ (state).data[FCS_FREECELLS_OFFSET+(f)] = (card);
+
+#define fcs_empty_freecell(state, f) \
+ fcs_put_card_in_freecell((state), (f), fcs_empty_card)
+
+#define fcs_card_set_num(card, num) \
+ (card) = (((card)&0xF0)|(num));
+
+#define fcs_card_set_suit(card, suit) \
+ (card) = (((card)&0x4F)|((suit)<<4));
+
+#define fcs_card_set_flipped(card, flipped) \
+ (card) = (((card)&((fcs_card_t)0x3F))|((fcs_card_t)((flipped)<<6)))
+
+#define fcs_card_get_flipped(card) \
+ ( (card) >> 6 )
+
+
+#ifdef FCS_WITH_TALONS
+#define fcs_talon_len(state) \
+ ((state).talon_params[0])
+
+#define fcs_talon_pos(state) \
+ ((state).talon_params[1])
+
+#define fcs_put_card_in_talon(state, pos, card) \
+ ((state).talon[pos] = (card))
+
+#define fcs_get_talon_card(state, pos) \
+ ((state).talon[pos])
+#endif
+
+#define fcs_flip_stack_card(state, s, c) \
+ (fcs_card_set_flipped(fcs_stack_card((state),(s),(c)), ((fcs_card_t)0) ))
+
+#define fcs_copy_stack(state, idx, buffer) {}
+
+#elif defined(INDIRECT_STACK_STATES) /* #ifdef DEBUG_STATES
+ #elif defined(COMPACT_STATES)
+ */
+
+typedef char fcs_card_t;
+
+struct fcs_struct_state_t
+{
+ fcs_card_t * stacks[MAX_NUM_STACKS];
+ fcs_card_t freecells[MAX_NUM_FREECELLS];
+ char foundations[MAX_NUM_DECKS*4];
+#ifdef FCS_WITH_TALONS
+ fcs_card_t * talon;
+ char talon_params[4];
+#endif
+};
+
+typedef struct fcs_struct_state_t fcs_state_t;
+
+#define fcs_card_card_num(card) \
+ ( (card) & 0x0F )
+
+#define fcs_card_suit(card) \
+ ( ((card) >> 4) & 0x03 )
+
+#define fcs_card_get_flipped(card) \
+ ( (card) >> 6 )
+
+#define fcs_standalone_stack_len(stack) \
+ ( (size_t)(stack[0]) )
+
+#define fcs_stack_len(state, s) \
+ ( (unsigned int)(state).stacks[(s)][0] )
+
+#define fcs_stack_card(state, s, c) \
+ ( (state).stacks[(s)][c+1] )
+
+#define fcs_stack_card_num(state, s, c) \
+ ( fcs_card_card_num(fcs_stack_card((state),(s),(c))) )
+
+#define fcs_stack_card_suit(state, s, c) \
+ ( fcs_card_suit(fcs_stack_card((state),(s),(c))) )
+
+#define fcs_freecell_card(state, f) \
+ ( (state).freecells[(f)] )
+
+#define fcs_freecell_card_num(state, f) \
+ ( fcs_card_card_num(fcs_freecell_card((state),(f))) )
+
+#define fcs_freecell_card_suit(state, f) \
+ ( fcs_card_suit(fcs_freecell_card((state),(f))) )
+
+#define fcs_foundation_value(state, d) \
+ ( (state).foundations[(d)] )
+
+#define fcs_increment_foundation(state, d) \
+ ( (state).foundations[(d)]++ )
+
+#define fcs_set_foundation(state, d, value) \
+ ( (state).foundations[(d)] = (value) )
+
+#define fcs_pop_stack_card(state, s, into) \
+ { \
+ into = fcs_stack_card((state), (s), (fcs_stack_len((state), (s))-1)); \
+ (state).stacks[s][fcs_stack_len((state), (s))] = fcs_empty_card; \
+ (state).stacks[s][0]--; \
+ }
+
+
+#define fcs_push_card_into_stack(state, ds, from) \
+ { \
+ (state).stacks[(ds)][fcs_stack_len((state), (ds))+1] = (from); \
+ (state).stacks[(ds)][0]++; \
+ }
+
+#define fcs_push_stack_card_into_stack(state, ds, ss, sc) \
+ fcs_push_card_into_stack((state), (ds), fcs_stack_card((state), (ss), (sc)))
+
+#define fcs_put_card_in_freecell(state, f, card) \
+ (state).freecells[(f)] = (card)
+
+#define fcs_empty_freecell(state, f) \
+ fcs_put_card_in_freecell((state), (f), fcs_empty_card)
+
+#define fcs_card_set_num(card, num) \
+ (card) = (((card)&0xF0)|(num))
+
+#define fcs_card_set_suit(card, suit) \
+ (card) = (((card)&0x4F)|((suit)<<4))
+
+#define fcs_card_set_flipped(card, flipped) \
+ (card) = (fcs_card_t)(((card)&0x3F)|((fcs_card_t)(flipped<<6)))
+
+#ifdef FCS_WITH_TALONS
+#define fcs_talon_len(state) \
+ ((state).talon_params[0])
+
+#define fcs_talon_pos(state) \
+ ((state).talon_params[1])
+
+#define fcs_put_card_in_talon(state, pos, card) \
+ ((state).talon[pos] = (card))
+
+#define fcs_get_talon_card(state, pos) \
+ ((state).talon[pos])
+#endif
+
+#define fcs_flip_stack_card(state, s, c) \
+ (fcs_card_set_flipped(fcs_stack_card(state,s,c), ((fcs_card_t)0) ))
+
+
+#define fcs_duplicate_state(dest,src) \
+ { \
+ (dest) = (src); \
+ (dest).stacks_copy_on_write_flags = 0; \
+ }
+
+#define fcs_copy_stack(state, idx, buffer) \
+ { \
+ if (! ((state).stacks_copy_on_write_flags & (1 << idx))) \
+ { \
+ size_t stack_len; \
+ (state).stacks_copy_on_write_flags |= (1 << idx); \
+ stack_len = fcs_stack_len((state).s,idx); \
+ memcpy(&buffer[idx << 7], (state).s.stacks[idx], stack_len+1); \
+ (state).s.stacks[idx] = &buffer[idx << 7]; \
+ } \
+ }
+
+
+typedef char fcs_locs_t;
+
+#endif /* #ifdef DEBUG_STATES -
+ #elif defined COMPACT_STATES -
+ #elif defined INDIRECT_STACK_STATES
+ */
+
+struct fcs_struct_state_with_locations_t
+{
+ fcs_state_t s;
+ fcs_locs_t stack_locs[MAX_NUM_STACKS];
+ fcs_locs_t fc_locs[MAX_NUM_FREECELLS];
+ struct fcs_struct_state_with_locations_t * parent;
+ fcs_move_stack_t * moves_to_parent;
+ int depth;
+ /*
+ * This field contains global, scan-independant flags, which are used
+ * from the FCS_VISITED_T enum below.
+ *
+ * FCS_VISITED_VISITED - deprecated
+ *
+ * FCS_VISITED_IN_SOLUTION_PATH - indicates that the state is in the
+ * solution path found by the scan. (used by the optimization scan)
+ *
+ * FCS_VISITED_IN_OPTIMIZED_PATH - indicates that the state is in the
+ * optimized solution path which is computed by the optimization scan.
+ *
+ * FCS_VISITED_DEAD_END - indicates that the state does not lead to
+ * anywhere useful, and scans should not examine it in the first place.
+ * */
+ int visited;
+ /*
+ * The iteration in which this state was marked as visited
+ * */
+ int visited_iter;
+ /*
+ * This is the number of direct children of this state which were not
+ * yet declared as dead ends. Once this counter reaches zero, this
+ * state too is declared as a dead end.
+ * */
+ int num_active_children;
+ /*
+ * This is a vector of flags - one for each scan. Each indicates whether
+ * its scan has already visited this state
+ * */
+ int scan_visited[MAX_NUM_SCANS_BUCKETS];
+#ifdef INDIRECT_STACK_STATES
+ /*
+ * A vector of flags that indicates which stacks were already copied.
+ * */
+ int stacks_copy_on_write_flags;
+#endif
+};
+
+typedef struct fcs_struct_state_with_locations_t fcs_state_with_locations_t;
+
+
+extern fcs_card_t freecell_solver_empty_card;
+#define fcs_empty_card freecell_solver_empty_card
+
+
+#ifdef FCS_WITH_TALONS
+#define fcs_klondike_talon_len(state) \
+ ((state).talon[0])
+
+#define fcs_klondike_talon_stack_pos(state) \
+ ((state).talon_params[0])
+
+#define fcs_klondike_talon_queue_pos(state) \
+ ((state).talon_params[1])
+
+#define fcs_klondike_talon_num_redeals_left(state) \
+ ((state).talon_params[2])
+
+#define fcs_klondike_talon_get_top_card(state) \
+ ((state).talon[(int)fcs_klondike_talon_stack_pos(state)])
+
+#define fcs_klondike_talon_queue_to_stack(state) \
+ ( ((state).talon[(int)((++fcs_klondike_talon_stack_pos(state))+1)]) = \
+ ((state).talon[(int)((fcs_klondike_talon_queue_pos(state)++)+1)]) )
+
+#define fcs_klondike_talon_redeal_bare(state) \
+ { \
+ fcs_klondike_talon_stack_pos(state) = -1; \
+ fcs_klondike_talon_queue_pos(state) = 0; \
+ }
+
+#define fcs_klondike_talon_decrement_stack(state) \
+ ((state).talon[(int)((fcs_klondike_talon_stack_pos(state)--)+1)] = fcs_empty_card)
+#endif
+
+
+extern void freecell_solver_canonize_state(
+ fcs_state_with_locations_t * state,
+ int freecells_num,
+ int stacks_num
+ );
+
+#define fcs_canonize_state(state,freecells_num,stacks_num) freecell_solver_canonize_state((state),(freecells_num),(stacks_num))
+
+#if (FCS_STATE_STORAGE != FCS_STATE_STORAGE_INDIRECT)
+
+#if (FCS_STATE_STORAGE != FCS_STATE_STORAGE_LIBREDBLACK_TREE)
+typedef void * fcs_compare_context_t;
+#else
+typedef const void * fcs_compare_context_t;
+#endif
+
+extern int freecell_solver_state_compare(const void * s1, const void * s2);
+extern int freecell_solver_state_compare_equal(const void * s1, const void * s2);
+extern int freecell_solver_state_compare_with_context(const void * s1, const void * s2, fcs_compare_context_t context);
+#else
+extern int freecell_solver_state_compare_indirect(const void * s1, const void * s2);
+extern int freecell_solver_state_compare_indirect_with_context(const void * s1, const void * s2, void * context);
+#endif
+
+#ifdef FCS_WITH_TALONS
+extern int fcs_talon_compare_with_context(const void * s1, const void * s2, fcs_compare_context_t context);
+#endif
+
+enum FCS_USER_STATE_TO_C_RETURN_CODES
+{
+ FCS_USER_STATE_TO_C__SUCCESS = 0,
+ FCS_USER_STATE_TO_C__PREMATURE_END_OF_INPUT
+};
+
+int freecell_solver_initial_user_state_to_c(
+ const char * string,
+ fcs_state_with_locations_t * out_state,
+ int freecells_num,
+ int stacks_num,
+ int decks_num
+#ifdef FCS_WITH_TALONS
+ ,int talon_type
+#endif
+#ifdef INDIRECT_STACK_STATES
+ , fcs_card_t * indirect_stacks_buffer
+#endif
+ );
+
+
+extern char * freecell_solver_state_as_string(
+ fcs_state_with_locations_t * state,
+ int freecells_num,
+ int stacks_num,
+ int decks_num,
+ int parseable_output,
+ int canonized_order_output,
+ int display_10_as_t
+ );
+
+enum FCS_STATE_VALIDITY_CODES
+{
+ FCS_STATE_VALIDITY__OK = 0,
+ FCS_STATE_VALIDITY__EMPTY_SLOT = 3,
+ FCS_STATE_VALIDITY__EXTRA_CARD = 2,
+ FCS_STATE_VALIDITY__MISSING_CARD = 1,
+ FCS_STATE_VALIDITY__PREMATURE_END_OF_INPUT = 4
+};
+
+extern int freecell_solver_check_state_validity(
+ fcs_state_with_locations_t * state,
+ int freecells_num,
+ int stacks_num,
+ int decks_num,
+#ifdef FCS_WITH_TALONS
+ int talon_type,
+#endif
+ fcs_card_t * misplaced_card
+ );
+
+#ifdef __cplusplus
+}
+#endif
+
+enum FCS_VISITED_T
+{
+ FCS_VISITED_VISITED = 0x1,
+ FCS_VISITED_IN_SOLUTION_PATH = 0x2,
+ FCS_VISITED_IN_OPTIMIZED_PATH = 0x4,
+ FCS_VISITED_DEAD_END = 0x8,
+ FCS_VISITED_ALL_TESTS_DONE = 0x10
+};
+
+
+#endif /* FC_SOLVE__STATE_H */
diff --git a/kpat/freecell-solver/test_arr.h b/kpat/freecell-solver/test_arr.h
new file mode 100644
index 00000000..cfc5cd12
--- /dev/null
+++ b/kpat/freecell-solver/test_arr.h
@@ -0,0 +1,136 @@
+/*
+ * test_arr.h - header file for some routines and macros involving tests and
+ * the like for Freecell Solver.
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2002
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ * */
+
+#ifndef FC_SOLVE__TEST_ARR_H
+#define FC_SOLVE__TEST_ARR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef int (*freecell_solver_solve_for_state_test_t)(
+ freecell_solver_soft_thread_t *,
+ fcs_state_with_locations_t *,
+ int,
+ int,
+ fcs_derived_states_list_t *,
+ int
+ );
+
+extern freecell_solver_solve_for_state_test_t freecell_solver_sfs_tests[FCS_TESTS_NUM];
+
+/*
+ * This macro determines if child can be placed above parent.
+ *
+ * The variable sequences_are_built_by has to be initialized to
+ * the sequences_are_built_by member of the instance.
+ *
+ * */
+#define fcs_is_parent_card(child, parent) \
+ ((fcs_card_card_num(child)+1 == fcs_card_card_num(parent)) && \
+ ((sequences_are_built_by == FCS_SEQ_BUILT_BY_RANK) ? \
+ 1 : \
+ ((sequences_are_built_by == FCS_SEQ_BUILT_BY_SUIT) ? \
+ (fcs_card_suit(child) == fcs_card_suit(parent)) : \
+ ((fcs_card_suit(child) & 0x1) != (fcs_card_suit(parent)&0x1)) \
+ )) \
+ )
+
+/*
+ * This macro traces the path of the state up to the original state,
+ * and thus calculates its real depth.
+ *
+ * It then assigns the newly updated depth throughout the path.
+ *
+ * */
+#define calculate_real_depth(ptr_state_orig) \
+{ \
+ if (calc_real_depth) \
+ { \
+ int this_real_depth = 0; \
+ fcs_state_with_locations_t * ptr_state = (ptr_state_orig); \
+ /* Count the number of states until the original state. */ \
+ while(ptr_state != NULL) \
+ { \
+ ptr_state = ptr_state->parent; \
+ this_real_depth++; \
+ } \
+ this_real_depth--; \
+ ptr_state = (ptr_state_orig); \
+ /* Assign the new depth throughout the path*/ \
+ while (ptr_state->depth != this_real_depth) \
+ { \
+ ptr_state->depth = this_real_depth; \
+ this_real_depth--; \
+ ptr_state = ptr_state->parent; \
+ } \
+ } \
+} \
+
+/*
+ * This macro marks a state as a dead end, and afterwards propogates
+ * this information to its parent and ancestor states.
+ * */
+#define mark_as_dead_end(ptr_state_input) \
+{ \
+ if (scans_synergy) \
+ { \
+ fcs_state_with_locations_t * ptr_state = (ptr_state_input); \
+ /* Mark as a dead end */ \
+ ptr_state->visited |= FCS_VISITED_DEAD_END; \
+ ptr_state = ptr_state->parent; \
+ if (ptr_state != NULL) \
+ { \
+ /* Decrease the refcount of the state */ \
+ ptr_state->num_active_children--; \
+ while((ptr_state->num_active_children == 0) && (ptr_state->visited & FCS_VISITED_ALL_TESTS_DONE)) \
+ { \
+ /* Mark as dead end */ \
+ ptr_state->visited |= FCS_VISITED_DEAD_END; \
+ /* Go to its parent state */ \
+ ptr_state = ptr_state->parent; \
+ if (ptr_state == NULL) \
+ { \
+ break; \
+ } \
+ /* Decrease the refcount */ \
+ ptr_state->num_active_children--; \
+ } \
+ } \
+ } \
+}
+
+/*
+ * This macro checks if we need to terminate from running this soft
+ * thread and return to the soft thread manager with an
+ * FCS_STATE_SUSPEND_PROCESS
+ * */
+#define check_if_limits_exceeded() \
+ ( \
+ ((instance->max_num_times >= 0) && \
+ (instance->num_times >= instance->max_num_times)) \
+ || \
+ ((hard_thread->ht_max_num_times >= 0) && \
+ (hard_thread->num_times >= hard_thread->ht_max_num_times)) \
+ || \
+ ((hard_thread->max_num_times >= 0) && \
+ (hard_thread->num_times >= hard_thread->max_num_times)) \
+ || \
+ ((instance->max_num_states_in_collection >= 0) && \
+ (instance->num_states_in_collection >= \
+ instance->max_num_states_in_collection) \
+ ) \
+ )
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/kpat/freecell-solver/tests.h b/kpat/freecell-solver/tests.h
new file mode 100644
index 00000000..ce0b35b5
--- /dev/null
+++ b/kpat/freecell-solver/tests.h
@@ -0,0 +1,307 @@
+/*
+ * fcs.h - header file of the test functions for Freecell Solver.
+ *
+ * The test functions code is found in freecell.c
+ *
+ * Written by Shlomi Fish (shlomif@vipe.technion.ac.il), 2000
+ *
+ * This file is in the public domain (it's uncopyrighted).
+ */
+
+#ifndef FC_SOLVE__TESTS_H
+#define FC_SOLVE__TESTS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
+
+#include "fcs_isa.h"
+#include "fcs.h"
+
+#include "test_arr.h"
+
+
+/*
+ * The number of cards that can be moved is
+ * (freecells_number + 1) * 2 ^ (free_stacks_number)
+ *
+ * See the Freecell FAQ and the source code of PySol
+ *
+ * */
+#define calc_max_sequence_move(fc_num, fs_num) \
+ ((instance->empty_stacks_fill == FCS_ES_FILLED_BY_ANY_CARD) ? \
+ ( \
+ (instance->unlimited_sequence_move) ? \
+ INT_MAX : \
+ (((fc_num)+1)<<(fs_num)) \
+ ) : \
+ ((fc_num)+1) \
+ )
+
+#include "caas.h"
+
+/*
+ * These are some macros to make it easier for the programmer.
+ * */
+#define state_with_locations (*ptr_state_with_locations)
+#define state (ptr_state_with_locations->s)
+#define new_state_with_locations (*ptr_new_state_with_locations)
+#define new_state (ptr_new_state_with_locations->s)
+
+#define sfs_check_state_begin() \
+ fcs_state_ia_alloc_into_var(ptr_new_state_with_locations, hard_thread); \
+ fcs_duplicate_state(new_state_with_locations, state_with_locations); \
+ /* Some A* and BFS parameters that need to be initialized in \
+ * the derived state. \
+ * */ \
+ ptr_new_state_with_locations->parent = ptr_state_with_locations; \
+ ptr_new_state_with_locations->moves_to_parent = moves; \
+ /* Make sure depth is consistent with the game graph. \
+ * I.e: the depth of every newly discovered state is derived from \
+ * the state from which it was discovered. */ \
+ ptr_new_state_with_locations->depth = ptr_state_with_locations->depth + 1; \
+ /* Mark this state as a state that was not yet visited */ \
+ ptr_new_state_with_locations->visited = 0; \
+ /* It's a newly created state which does not have children yet. */ \
+ ptr_new_state_with_locations->num_active_children = 0; \
+ memset(ptr_new_state_with_locations->scan_visited, '\0', \
+ sizeof(ptr_new_state_with_locations->scan_visited) \
+ ); \
+ fcs_move_stack_reset(moves); \
+
+
+
+
+#define sfs_check_state_end() \
+/* The last move in a move stack should be FCS_MOVE_TYPE_CANONIZE \
+ * because it indicates that the order of the stacks and freecells \
+ * need to be recalculated \
+ * */ \
+fcs_move_set_type(temp_move,FCS_MOVE_TYPE_CANONIZE); \
+fcs_move_stack_push(moves, temp_move); \
+ \
+{ \
+ fcs_state_with_locations_t * existing_state; \
+ check = freecell_solver_check_and_add_state( \
+ soft_thread, \
+ ptr_new_state_with_locations, \
+ &existing_state \
+ ); \
+ if ((check == FCS_STATE_BEGIN_SUSPEND_PROCESS) || \
+ (check == FCS_STATE_SUSPEND_PROCESS)) \
+ { \
+ /* This state is not going to be used, so \
+ * let's clean it. */ \
+ fcs_state_ia_release(hard_thread); \
+ return check; \
+ } \
+ else if (check == FCS_STATE_ALREADY_EXISTS) \
+ { \
+ fcs_state_ia_release(hard_thread); \
+ calculate_real_depth(existing_state); \
+ /* Re-parent the existing state to this one. \
+ * \
+ * What it means is that if the depth of the state if it \
+ * can be reached from this one is lower than what it \
+ * already have, then re-assign its parent to this state. \
+ * */ \
+ if (reparent && \
+ (existing_state->depth > ptr_state_with_locations->depth+1)) \
+ { \
+ /* Make a copy of "moves" because "moves" will be destroyed */\
+ existing_state->moves_to_parent = \
+ freecell_solver_move_stack_compact_allocate( \
+ hard_thread, moves \
+ ); \
+ if (!(existing_state->visited & FCS_VISITED_DEAD_END)) \
+ { \
+ if ((--existing_state->parent->num_active_children) == 0) \
+ { \
+ mark_as_dead_end( \
+ existing_state->parent \
+ ); \
+ } \
+ ptr_state_with_locations->num_active_children++; \
+ } \
+ existing_state->parent = ptr_state_with_locations; \
+ existing_state->depth = ptr_state_with_locations->depth + 1; \
+ } \
+ fcs_derived_states_list_add_state( \
+ derived_states_list, \
+ existing_state \
+ ); \
+ } \
+ else \
+ { \
+ fcs_derived_states_list_add_state( \
+ derived_states_list, \
+ ptr_new_state_with_locations \
+ ); \
+ } \
+}
+
+
+/*
+ This macro checks if the top card in the stack is a flipped card
+ , and if so flips it so its face is up.
+ */
+#define fcs_flip_top_card(stack) \
+{ \
+ int cards_num; \
+ cards_num = fcs_stack_len(new_state,stack); \
+ \
+ if (cards_num > 0) \
+ { \
+ if (fcs_card_get_flipped( \
+ fcs_stack_card( \
+ new_state, \
+ stack, \
+ cards_num-1) \
+ ) == 1 \
+ ) \
+ { \
+ fcs_flip_stack_card(new_state,stack,cards_num-1); \
+ fcs_move_set_type(temp_move, FCS_MOVE_TYPE_FLIP_CARD); \
+ fcs_move_set_src_stack(temp_move, stack); \
+ \
+ fcs_move_stack_push(moves, temp_move); \
+ } \
+ } \
+}
+
+
+/*
+ * dest is the destination stack
+ * source is the source stack
+ * start is the start height
+ * end is the end height
+ * a is the iterator
+ * */
+#define fcs_move_sequence(dest, source, start, end, a) \
+{ \
+ for ( a = (start) ; a <= (end) ; a++) \
+ { \
+ fcs_push_stack_card_into_stack(new_state, dest, source, a); \
+ } \
+ \
+ for ( a = (start) ; a <= (end) ; a++) \
+ { \
+ fcs_pop_stack_card(new_state, source, temp_card); \
+ } \
+ \
+ fcs_move_set_type(temp_move, FCS_MOVE_TYPE_STACK_TO_STACK); \
+ fcs_move_set_src_stack(temp_move, source); \
+ fcs_move_set_dest_stack(temp_move, dest); \
+ fcs_move_set_num_cards_in_seq(temp_move, (end)-(start)+1); \
+ \
+ fcs_move_stack_push(moves, temp_move); \
+}
+
+/*
+ * This test declares a few access variables that are used in all
+ * the tests.
+ * */
+#define tests_declare_accessors() \
+ freecell_solver_hard_thread_t * hard_thread; \
+ freecell_solver_instance_t * instance; \
+ fcs_state_with_locations_t * ptr_new_state_with_locations; \
+ fcs_move_stack_t * moves; \
+ char * indirect_stacks_buffer; \
+ int calc_real_depth; \
+ int scans_synergy
+
+/*
+ * This macro defines these accessors to have some value.
+ * */
+#define tests_define_accessors() \
+ hard_thread = soft_thread->hard_thread; \
+ instance = hard_thread->instance; \
+ moves = hard_thread->reusable_move_stack; \
+ indirect_stacks_buffer = hard_thread->indirect_stacks_buffer; \
+ calc_real_depth = instance->calc_real_depth; \
+ scans_synergy = instance->scans_synergy;
+
+
+
+extern int freecell_solver_sfs_simple_simon_move_sequence_to_founds(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ );
+extern int freecell_solver_sfs_simple_simon_move_sequence_to_true_parent(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ );
+
+extern int freecell_solver_sfs_simple_simon_move_whole_stack_sequence_to_false_parent(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ );
+
+extern int freecell_solver_sfs_simple_simon_move_sequence_to_true_parent_with_some_cards_above(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ );
+
+extern int freecell_solver_sfs_simple_simon_move_sequence_with_some_cards_above_to_true_parent(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ );
+
+extern int freecell_solver_sfs_simple_simon_move_sequence_with_junk_seq_above_to_true_parent_with_some_cards_above(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ );
+
+extern int freecell_solver_sfs_simple_simon_move_whole_stack_sequence_to_false_parent_with_some_cards_above(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ );
+
+extern int freecell_solver_sfs_simple_simon_move_sequence_to_parent_on_the_same_stack(
+ freecell_solver_soft_thread_t * soft_thread,
+ fcs_state_with_locations_t * ptr_state_with_locations,
+ int num_freestacks,
+ int num_freecells,
+ fcs_derived_states_list_t * derived_states_list,
+ int reparent
+ );
+
+#ifdef __cplusplus
+}
+#endif
+
+#define my_copy_stack(idx) fcs_copy_stack(new_state_with_locations, idx, indirect_stacks_buffer);
+
+#endif /* FC_SOLVE__TESTS_H */
diff --git a/kpat/freecell.cpp b/kpat/freecell.cpp
new file mode 100644
index 00000000..7a72c1fc
--- /dev/null
+++ b/kpat/freecell.cpp
@@ -0,0 +1,854 @@
+/*---------------------------------------------------------------------------
+
+ freecell.cpp implements a patience card game
+
+ Copyright (C) 1997 Rodolfo Borges
+ (C) 2000 Stephan Kulow
+
+ * 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 "freecell.h"
+#include <klocale.h>
+#include "deck.h"
+#include <assert.h>
+#include <kdebug.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <qtimer.h>
+#include "cardmaps.h"
+
+#include "freecell-solver/fcs_user.h"
+#include "freecell-solver/fcs_cl.h"
+
+const int CHUNKSIZE = 100;
+
+void FreecellPile::moveCards(CardList &c, Pile *to)
+{
+ if (c.count() == 1) {
+ Pile::moveCards(c, to);
+ return;
+ }
+ FreecellBase *b = dynamic_cast<FreecellBase*>(dealer());
+ if (b) {
+ b->moveCards(c, this, to);
+ }
+}
+
+//-------------------------------------------------------------------------//
+
+FreecellBase::FreecellBase( int decks, int stores, int freecells, int fill, bool unlimit,
+ KMainWindow* parent, const char* name)
+ : Dealer(parent,name),
+solver_instance(0), es_filling(fill), solver_ret(FCS_STATE_NOT_BEGAN_YET),
+unlimited_move(unlimit)
+{
+ deck = Deck::new_deck(this, decks);
+ deck->hide();
+
+ kdDebug(11111) << "cards " << deck->cards().count() << endl;
+ Pile *t;
+ for (int i = 0; i < stores; i++) {
+ FreecellPile *p = new FreecellPile(1 + i, this);
+ store.append(p);
+ p->setAddFlags(Pile::addSpread | Pile::several);
+ p->setRemoveFlags(Pile::several);
+ p->setCheckIndex(0);
+ }
+
+ for (int i = 0; i < freecells; i++)
+ {
+ t = new Pile (1 + stores +i, this);
+ freecell.append(t);
+ t->setType(Pile::FreeCell);
+ }
+
+ for (int i = 0; i < decks * 4; i++)
+ {
+ t = new Pile(1 + stores + freecells +i, this);
+ target.append(t);
+ t->setType(Pile::KlondikeTarget);
+ // COOLO: I'm still not too sure about that t->setRemoveFlags(Pile::Default);
+ }
+
+ setActions(Dealer::Demo | Dealer::Hint);
+}
+
+FreecellBase::~FreecellBase()
+{
+ if (solver_instance)
+ {
+ freecell_solver_user_free(solver_instance);
+ solver_instance = NULL;
+ }
+}
+//-------------------------------------------------------------------------//
+
+void FreecellBase::restart()
+{
+ freeSolution();
+ deck->collectAndShuffle();
+ deal();
+}
+
+QString suitToString(Card::Suit s) {
+ switch (s) {
+ case Card::Clubs:
+ return "C";
+ case Card::Hearts:
+ return "H";
+ case Card::Diamonds:
+ return "D";
+ case Card::Spades:
+ return "S";
+ }
+ return QString::null;
+}
+
+QString rankToString(Card::Rank r)
+{
+ switch (r) {
+ case Card::King:
+ return "K";
+ case Card::Ace:
+ return "A";
+ case Card::Jack:
+ return "J";
+ case Card::Queen:
+ return "Q";
+ default:
+ return QString::number(r);
+ }
+}
+
+int getDeck(Card::Suit suit)
+{
+ switch (suit) {
+ case Card::Hearts:
+ return 0;
+ case Card::Spades:
+ return 1;
+ case Card::Diamonds:
+ return 2;
+ case Card::Clubs:
+ return 3;
+ }
+ return 0;
+}
+
+static const char * freecell_solver_cmd_line_args[280] =
+{
+"--method", "soft-dfs", "-to", "0123456789", "-step",
+"500", "--st-name", "1", "-nst", "--method",
+"soft-dfs", "-to", "0123467", "-step", "500",
+"--st-name", "2", "-nst", "--method", "random-dfs",
+"-seed", "2", "-to", "0[01][23456789]", "-step",
+"500", "--st-name", "3", "-nst", "--method",
+"random-dfs", "-seed", "1", "-to", "0[0123456789]",
+"-step", "500", "--st-name", "4", "-nst", "--method",
+"random-dfs", "-seed", "3", "-to", "0[01][23467]",
+"-step", "500", "--st-name", "5", "-nst", "--method",
+"random-dfs", "-seed", "4", "-to", "0[0123467]",
+"-step", "500", "--st-name", "9", "-nst", "--method",
+"random-dfs", "-to", "[01][23456789]", "-seed", "8",
+"-step", "500", "--st-name", "10", "-nst",
+"--method", "random-dfs", "-to", "[01][23456789]",
+"-seed", "268", "-step", "500", "--st-name", "12",
+"-nst", "--method", "a-star", "-asw",
+"0.2,0.3,0.5,0,0", "-step", "500", "--st-name", "16",
+"-nst", "--method", "a-star", "-to", "0123467",
+"-asw", "0.5,0,0.3,0,0", "-step", "500", "--st-name",
+"18", "-nst", "--method", "soft-dfs", "-to",
+"0126394875", "-step", "500", "--st-name", "19",
+"--prelude",
+"350@2,350@5,350@9,350@12,350@2,350@10,350@3,350@9,350@5,350@18,350@2,350@5,350@4,350@10,350@4,350@12,1050@9,700@18,350@10,350@5,350@2,350@10,1050@16,350@2,700@4,350@10,1050@2,1400@3,350@18,1750@5,350@16,350@18,700@4,1050@12,2450@5,1400@18,1050@2,1400@10,6300@1,4900@12,8050@18",
+"-ni", "--method", "soft-dfs", "-to", "01ABCDE",
+"-step", "500", "--st-name", "0", "-nst", "--method",
+"random-dfs", "-to", "[01][ABCDE]", "-seed", "1",
+"-step", "500", "--st-name", "1", "-nst", "--method",
+"random-dfs", "-to", "[01][ABCDE]", "-seed", "2",
+"-step", "500", "--st-name", "2", "-nst", "--method",
+"random-dfs", "-to", "[01][ABCDE]", "-seed", "3",
+"-step", "500", "--st-name", "3", "-nst", "--method",
+"random-dfs", "-to", "01[ABCDE]", "-seed", "268",
+"-step", "500", "--st-name", "4", "-nst", "--method",
+"a-star", "-to", "01ABCDE", "-step", "500",
+"--st-name", "5", "-nst", "--method", "a-star",
+"-to", "01ABCDE", "-asw", "0.2,0.3,0.5,0,0", "-step",
+"500", "--st-name", "6", "-nst", "--method",
+"a-star", "-to", "01ABCDE", "-asw", "0.5,0,0.5,0,0",
+"-step", "500", "--st-name", "7", "-nst", "--method",
+"random-dfs", "-to", "[01][ABD][CE]", "-seed", "1900",
+"-step", "500", "--st-name", "8", "-nst", "--method",
+"random-dfs", "-to", "[01][ABCDE]", "-seed", "192",
+"-step", "500", "--st-name", "9", "-nst", "--method",
+"random-dfs", "-to", "[01ABCDE]", "-seed", "1977",
+"-step", "500", "--st-name", "10", "-nst",
+"--method", "random-dfs", "-to", "[01ABCDE]", "-seed",
+"24", "-step", "500", "--st-name", "11", "-nst",
+"--method", "soft-dfs", "-to", "01ABDCE", "-step",
+"500", "--st-name", "12", "-nst", "--method",
+"soft-dfs", "-to", "ABC01DE", "-step", "500",
+"--st-name", "13", "-nst", "--method", "soft-dfs",
+"-to", "01EABCD", "-step", "500", "--st-name", "14",
+"-nst", "--method", "soft-dfs", "-to", "01BDAEC",
+"-step", "500", "--st-name", "15", "--prelude",
+"1000@0,1000@3,1000@0,1000@9,1000@4,1000@9,1000@3,1000@4,2000@2,1000@0,2000@1,1000@14,2000@11,1000@14,1000@3,1000@11,1000@2,1000@0,2000@4,2000@10,1000@0,1000@2,2000@10,1000@0,2000@11,2000@1,1000@10,1000@2,1000@10,2000@0,1000@9,1000@1,1000@2,1000@14,3000@8,1000@2,1000@14,1000@1,1000@10,3000@6,2000@4,1000@2,2000@0,1000@2,1000@11,2000@6,1000@0,5000@1,1000@0,2000@1,1000@2,3000@3,1000@10,1000@14,2000@6,1000@0,1000@2,2000@11,6000@8,8000@9,3000@1,2000@10,2000@14,3000@15,4000@0,1000@8,1000@10,1000@14,7000@0,14000@2,6000@3,7000@4,1000@8,4000@9,2000@15,2000@6,4000@3,2000@4,3000@15,2000@0,6000@1,2000@4,4000@6,4000@9,4000@14,7000@8,3000@0,3000@1,5000@2,3000@3,4000@9,8000@10,9000@3,5000@8,7000@11,11000@12,12000@0,8000@3,11000@9,9000@15,7000@2,12000@8,16000@5,8000@13,18000@0,9000@15,12000@10,16000@0,14000@3,16000@9,26000@4,23000@3,42000@6,22000@8,27000@10,38000@7,41000@0,42000@3,84000@13,61000@15,159000@5,90000@9"
+};
+
+void FreecellBase::findSolution()
+{
+ kdDebug(11111) << "findSolution\n";
+
+ QString output = solverFormat();
+ kdDebug(11111) << output << endl;
+
+ int ret;
+
+ /* If solver_instance was not initialized yet - initialize it */
+ if (! solver_instance)
+ {
+ solver_instance = freecell_solver_user_alloc();
+
+ char * error_string;
+ int error_arg;
+ char * known_parameters[1] = {NULL};
+
+
+ ret = freecell_solver_user_cmd_line_parse_args(
+ solver_instance,
+ sizeof(freecell_solver_cmd_line_args)/sizeof(freecell_solver_cmd_line_args[0]),
+ freecell_solver_cmd_line_args,
+ 0,
+ known_parameters,
+ NULL,
+ NULL,
+ &error_string,
+ &error_arg
+ );
+
+
+ assert(!ret);
+ }
+ /*
+ * 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
+ * */
+#if 0
+ ret = freecell_solver_user_set_game(solver_instance,
+ freecell.count(),
+ store.count(),
+ deck->decksNum(),
+ FCS_SEQ_BUILT_BY_ALTERNATE_COLOR,
+ unlimited_move,
+ es_filling);
+ assert(!ret);
+#else
+ freecell_solver_user_set_num_freecells(solver_instance,freecell.count());
+ freecell_solver_user_set_num_stacks(solver_instance,store.count());
+ freecell_solver_user_set_num_decks(solver_instance,deck->decksNum());
+ 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, unlimited_move);
+ freecell_solver_user_set_empty_stacks_filled_by(solver_instance, es_filling);
+
+#endif
+
+ freecell_solver_user_limit_iterations(solver_instance, CHUNKSIZE);
+
+ solver_ret = freecell_solver_user_solve_board(solver_instance,
+ output.latin1());
+ resumeSolution();
+}
+
+void FreecellBase::resumeSolution()
+{
+ if (!solver_instance)
+ return;
+
+ emit gameInfo(i18n("%1 tries - depth %2")
+ .arg(freecell_solver_user_get_num_times(solver_instance))
+ .arg(freecell_solver_user_get_current_depth(solver_instance)));
+
+ if (solver_ret == FCS_STATE_WAS_SOLVED)
+ {
+ emit gameInfo(i18n("solved after %1 tries").
+ arg(freecell_solver_user_get_num_times(
+ solver_instance)));
+ kdDebug(11111) << "solved\n";
+ Dealer::demo();
+ return;
+ }
+ if (solver_ret == FCS_STATE_IS_NOT_SOLVEABLE) {
+ int moves = freecell_solver_user_get_num_times(solver_instance);
+ freeSolution();
+ emit gameInfo(i18n("unsolved after %1 moves")
+ .arg(moves));
+ stopDemo();
+ return;
+ }
+
+ unsigned int max_iters = freecell_solver_user_get_limit_iterations(
+ solver_instance) + CHUNKSIZE;
+ freecell_solver_user_limit_iterations(solver_instance,
+ max_iters);
+
+ if (max_iters > 120000) {
+ solver_ret = FCS_STATE_IS_NOT_SOLVEABLE;
+ resumeSolution();
+ return;
+ }
+
+ solver_ret = freecell_solver_user_resume_solution(solver_instance);
+ QTimer::singleShot(0, this, SLOT(resumeSolution()));
+
+}
+MoveHint *FreecellBase::translateMove(void *m) {
+ fcs_move_t move = *(static_cast<fcs_move_t *>(m));
+ uint cards = fcs_move_get_num_cards_in_seq(move);
+ Pile *from = 0;
+ Pile *to = 0;
+
+ switch(fcs_move_get_type(move))
+ {
+ case FCS_MOVE_TYPE_STACK_TO_STACK:
+ from = store[fcs_move_get_src_stack(move)];
+ to = store[fcs_move_get_dest_stack(move)];
+ break;
+
+ case FCS_MOVE_TYPE_FREECELL_TO_STACK:
+ from = freecell[fcs_move_get_src_freecell(move)];
+ to = store[fcs_move_get_dest_stack(move)];
+ cards = 1;
+ break;
+
+ case FCS_MOVE_TYPE_FREECELL_TO_FREECELL:
+ from = freecell[fcs_move_get_src_freecell(move)];
+ to = freecell[fcs_move_get_dest_freecell(move)];
+ cards = 1;
+ break;
+
+ case FCS_MOVE_TYPE_STACK_TO_FREECELL:
+ from = store[fcs_move_get_src_stack(move)];
+ to = freecell[fcs_move_get_dest_freecell(move)];
+ cards = 1;
+ break;
+
+ case FCS_MOVE_TYPE_STACK_TO_FOUNDATION:
+ from = store[fcs_move_get_src_stack(move)];
+ cards = 1;
+ to = 0;
+ break;
+
+ case FCS_MOVE_TYPE_FREECELL_TO_FOUNDATION:
+ from = freecell[fcs_move_get_src_freecell(move)];
+ cards = 1;
+ to = 0;
+ }
+ assert(from);
+ assert(cards <= from->cards().count());
+ assert(to || cards == 1);
+ Card *c = from->cards()[from->cards().count() - cards];
+
+ if (!to)
+ to = findTarget(c);
+ assert(to);
+ return new MoveHint(c, to);
+}
+
+QString FreecellBase::solverFormat() const
+{
+ QString output;
+ QString tmp;
+ for (uint i = 0; i < target.count(); i++) {
+ if (target[i]->isEmpty())
+ continue;
+ tmp += suitToString(target[i]->top()->suit()) + "-" + rankToString(target[i]->top()->rank()) + " ";
+ }
+ if (!tmp.isEmpty())
+ output += QString::fromLatin1("Foundations: %1\n").arg(tmp);
+
+ tmp.truncate(0);
+ for (uint i = 0; i < freecell.count(); i++) {
+ if (freecell[i]->isEmpty())
+ tmp += "- ";
+ else
+ tmp += rankToString(freecell[i]->top()->rank()) + suitToString(freecell[i]->top()->suit()) + " ";
+ }
+ if (!tmp.isEmpty())
+ output += QString::fromLatin1("Freecells: %1\n").arg(tmp);
+
+ for (uint i = 0; i < store.count(); i++)
+ {
+ CardList cards = store[i]->cards();
+ for (CardList::ConstIterator it = cards.begin(); it != cards.end(); ++it)
+ output += rankToString((*it)->rank()) + suitToString((*it)->suit()) + " ";
+ output += "\n";
+ }
+ return output;
+}
+
+// Idea stolen from klondike.cpp
+//
+// This function returns true when it is certain that the card t is no longer
+// needed on any of the play piles.
+//
+// To determine wether a card is no longer needed on any of the play piles we
+// obviously must know what a card can be used for there. According to the
+// rules a card can be used to store another card with 1 less unit of value
+// and opposite color. This is the only thing that a card can be used for
+// there. Therefore the cards with lowest value (1) are useless there (base
+// case). The other cards each have 2 cards that can be stored on them, let us
+// call those 2 cards *depending cards*.
+//
+// The object of the game is to put all cards on the target piles. Therefore
+// cards that are no longer needed on any of the play piles should be put on
+// the target piles if possible. Cards on the target piles can not be moved
+// and they can not store any of its depending cards. Let us call this that
+// the cards on the target piles are *out of play*.
+//
+// The simple and obvious rule is:
+// A card is no longer needed when both of its depending cards are out of
+// play.
+//
+// More complex:
+// Assume card t is red. Now, if the lowest unplayed black card is
+// t.value()-2, then t may be needed to hold that black t.value()-1 card.
+// If the lowest unplayed black card is t.value()-1, it will be playable
+// to the target, unless it is needed for a red card of value t.value()-2.
+//
+// So, t is not needed if the lowest unplayed red card is t.value()-2 and the
+// lowest unplayed black card is t.value()-1, OR if the lowest unplayed black
+// card is t.value(). So, no recursion needed - we did it ahead of time.
+
+bool FreecellBase::noLongerNeeded(const Card & t)
+{
+
+ if (t.rank() <= Card::Two) return true; // Base case.
+
+ bool cardIsRed = t.isRed();
+
+ uint numSame = 0, numDiff = 0;
+ Card::Rank lowSame = Card::King, lowDiff = Card::King;
+ for (PileList::Iterator it = target.begin(); it != target.end(); ++it)
+ {
+ if ((*it)->isEmpty())
+ continue;
+ if ((*it)->top()->isRed() == cardIsRed) {
+ numSame++;
+ if ((*it)->top()->rank() < lowSame)
+ lowSame = static_cast<Card::Rank>((*it)->top()->rank()+1);
+ } else {
+ numDiff++;
+ if ((*it)->top()->rank() < lowDiff)
+ lowDiff = static_cast<Card::Rank>((*it)->top()->rank()+1);
+ }
+ }
+ if (numSame < target.count()/2) lowSame = Card::Ace;
+ if (numDiff < target.count()/2) lowDiff = Card::Ace;
+
+ return (lowDiff >= t.rank() ||
+ (lowDiff >= t.rank()-1 && lowSame >= t.rank()-2));
+}
+
+// This is the getHints() from dealer.cpp with one line changed
+// to use noLongerNeeded() to decide if the card should be
+// dropped or not.
+//
+// I would recommend adding a virtual bool noLongerNeeded(const Card &t)
+// to the base class (Dealer) that just returns true, and then calling
+// it like is done here. That would preserve current functionality
+// but eliminate this code duplication
+void FreecellBase::getHints()
+{
+ for (PileList::Iterator it = piles.begin(); it != piles.end(); ++it)
+ {
+ if (!takeTargetForHints() && (*it)->target())
+ continue;
+
+ Pile *store = *it;
+ if (store->isEmpty())
+ continue;
+// kdDebug(11111) << "trying " << store->top()->name() << endl;
+
+ CardList cards = store->cards();
+ while (cards.count() && !cards.first()->realFace()) cards.remove(cards.begin());
+
+ CardList::Iterator iti = cards.begin();
+ while (iti != cards.end())
+ {
+ if (store->legalRemove(*iti)) {
+// kdDebug(11111) << "could remove " << (*iti)->name() << endl;
+ for (PileList::Iterator pit = piles.begin(); pit != piles.end(); ++pit)
+ {
+ Pile *dest = *pit;
+ if (dest == store)
+ continue;
+ if (store->indexOf(*iti) == 0 && dest->isEmpty() && !dest->target())
+ continue;
+ if (!dest->legalAdd(cards))
+ continue;
+
+ bool old_prefer = checkPrefering( dest->checkIndex(), dest, cards );
+ if (!takeTargetForHints() && dest->target())
+ newHint(new MoveHint(*iti, dest, noLongerNeeded(*(*iti))));
+ else {
+ store->hideCards(cards);
+ // if it could be here as well, then it's no use
+ if ((store->isEmpty() && !dest->isEmpty()) || !store->legalAdd(cards))
+ newHint(new MoveHint(*iti, dest));
+ else {
+ if (old_prefer && !checkPrefering( store->checkIndex(),
+ store, cards ))
+ { // if checkPrefers says so, we add it nonetheless
+ newHint(new MoveHint(*iti, dest));
+ }
+ }
+ store->unhideCards(cards);
+ }
+ }
+ }
+ cards.remove(iti);
+ iti = cards.begin();
+ }
+ }
+}
+
+void FreecellBase::demo()
+{
+ if (solver_instance && solver_ret == FCS_STATE_WAS_SOLVED) {
+ Dealer::demo();
+ return;
+ }
+ towait = (Card*)-1;
+ unmarkAll();
+ kdDebug(11111) << "demo " << (solver_ret != FCS_STATE_IS_NOT_SOLVEABLE) << endl;
+ if (solver_ret != FCS_STATE_IS_NOT_SOLVEABLE)
+ findSolution();
+}
+
+MoveHint *FreecellBase::chooseHint()
+{
+ if (solver_instance && freecell_solver_user_get_moves_left(solver_instance)) {
+
+ emit gameInfo(i18n("%1 moves before finish").arg(freecell_solver_user_get_moves_left(solver_instance)));
+
+ fcs_move_t move;
+ if (!freecell_solver_user_get_next_move(solver_instance, &move)) {
+ MoveHint *mh = translateMove(&move);
+ oldmoves.append(mh);
+ return mh;
+ } else
+ return 0;
+ } else
+ return Dealer::chooseHint();
+}
+
+void FreecellBase::countFreeCells(int &free_cells, int &free_stores) const
+{
+ free_cells = 0;
+ free_stores = 0;
+
+ for (uint i = 0; i < freecell.count(); i++)
+ if (freecell[i]->isEmpty()) free_cells++;
+ if (es_filling == FCS_ES_FILLED_BY_ANY_CARD)
+ for (uint i = 0; i < store.count(); i++)
+ if (store[i]->isEmpty()) free_stores++;
+}
+
+void FreecellBase::freeSolution()
+{
+ for (HintList::Iterator it = oldmoves.begin(); it != oldmoves.end(); ++it)
+ delete *it;
+ oldmoves.clear();
+
+ if (!solver_instance)
+ return;
+ freecell_solver_user_recycle(solver_instance);
+ solver_ret = FCS_STATE_NOT_BEGAN_YET;
+}
+
+void FreecellBase::stopDemo()
+{
+ Dealer::stopDemo();
+ freeSolution();
+}
+
+void FreecellBase::moveCards(CardList &c, FreecellPile *from, Pile *to)
+{
+ if (!demoActive() && solver_instance) {
+ freeSolution();
+ }
+
+ assert(c.count() > 1);
+ if (unlimited_move) {
+ from->Pile::moveCards(c, to);
+ return;
+ }
+ setWaiting(true);
+
+ from->moveCardsBack(c);
+ waitfor = c.first();
+ connect(waitfor, SIGNAL(stoped(Card*)), SLOT(waitForMoving(Card*)));
+
+ PileList fcs;
+
+ for (uint i = 0; i < freecell.count(); i++)
+ if (freecell[i]->isEmpty()) fcs.append(freecell[i]);
+
+ PileList fss;
+
+ if (es_filling == FCS_ES_FILLED_BY_ANY_CARD)
+ for (uint i = 0; i < store.count(); i++)
+ if (store[i]->isEmpty() && to != store[i]) fss.append(store[i]);
+
+ if (fcs.count() == 0) {
+ assert(fss.count());
+ fcs.append(fss.last());
+ fss.remove(fss.fromLast());
+ }
+ while (moves.count()) { delete moves.first(); moves.remove(moves.begin()); }
+
+ movePileToPile(c, to, fss, fcs, 0, c.count(), 0);
+
+ if (!waitfor->animated())
+ QTimer::singleShot(0, this, SLOT(startMoving()));
+}
+
+struct MoveAway {
+ Pile *firstfree;
+ int start;
+ int count;
+};
+
+void FreecellBase::movePileToPile(CardList &c, Pile *to, PileList fss, PileList &fcs, uint start, uint count, int debug_level)
+{
+ kdDebug(11111) << debug_level << " movePileToPile" << c.count() << " " << start << " " << count << endl;
+ uint moveaway = 0;
+ if (count > fcs.count() + 1) {
+ moveaway = (fcs.count() + 1);
+ while (moveaway * 2 < count)
+ moveaway <<= 1;
+ }
+ kdDebug(11111) << debug_level << " moveaway " << moveaway << endl;
+
+ QValueList<MoveAway> moves_away;
+
+ if (count - moveaway < (fcs.count() + 1) && (count <= 2 * (fcs.count() + 1))) {
+ moveaway = count - (fcs.count() + 1);
+ }
+ while (count > fcs.count() + 1) {
+ assert(fss.count());
+ MoveAway ma;
+ ma.firstfree = fss[0];
+ ma.start = start;
+ ma.count = moveaway;
+ moves_away.append(ma);
+ fss.remove(fss.begin());
+ movePileToPile(c, ma.firstfree, fss, fcs, start, moveaway, debug_level + 1);
+ start += moveaway;
+ count -= moveaway;
+ moveaway >>= 1;
+ if ((count > (fcs.count() + 1)) && (count <= 2 * (fcs.count() + 1)))
+ moveaway = count - (fcs.count() + 1);
+ }
+ uint moving = QMIN(count, QMIN(c.count() - start, fcs.count() + 1));
+ assert(moving);
+
+ for (uint i = 0; i < moving - 1; i++) {
+ moves.append(new MoveHint(c[c.count() - i - 1 - start], fcs[i]));
+ }
+ moves.append(new MoveHint(c[c.count() - start - 1 - (moving - 1)], to));
+
+ for (int i = moving - 2; i >= 0; --i)
+ moves.append(new MoveHint(c[c.count() - i - 1 - start], to));
+
+ while (moves_away.count())
+ {
+ MoveAway ma = moves_away.last();
+ moves_away.remove(moves_away.fromLast());
+ movePileToPile(c, to, fss, fcs, ma.start, ma.count, debug_level + 1);
+ fss.append(ma.firstfree);
+ }
+}
+
+void FreecellBase::startMoving()
+{
+ kdDebug(11111) << "startMoving\n";
+ if (moves.isEmpty()) {
+ if (demoActive() && towait) {
+ waitForDemo(towait);
+ }
+ setWaiting(false);
+ takeState();
+ return;
+ }
+
+ MoveHint *mh = moves.first();
+ moves.remove(moves.begin());
+ CardList empty;
+ empty.append(mh->card());
+ assert(mh->card() == mh->card()->source()->top());
+ assert(mh->pile()->legalAdd(empty));
+ mh->pile()->add(mh->card());
+ mh->pile()->moveCardsBack(empty, true);
+ waitfor = mh->card();
+ kdDebug(11111) << "wait for moving end " << mh->card()->name() << endl;
+ connect(mh->card(), SIGNAL(stoped(Card*)), SLOT(waitForMoving(Card*)));
+ delete mh;
+}
+
+void FreecellBase::newDemoMove(Card *m)
+{
+ Dealer::newDemoMove(m);
+ if (m != m->source()->top())
+ m->disconnect();
+}
+
+void FreecellBase::waitForMoving(Card *c)
+{
+ if (waitfor != c)
+ return;
+ c->disconnect();
+ startMoving();
+}
+
+bool FreecellBase::cardDblClicked(Card *c)
+{
+ // target move
+ if (Dealer::cardDblClicked(c))
+ return true;
+
+ if (c->animated())
+ return false;
+
+ if (c == c->source()->top() && c->realFace())
+ for (uint i = 0; i < freecell.count(); i++)
+ if (freecell[i]->isEmpty()) {
+ CardList empty;
+ empty.append(c);
+ c->source()->moveCards(empty, freecell[i]);
+ canvas()->update();
+ return true;
+ }
+ return false;
+}
+
+bool FreecellBase::CanPutStore(const Pile *c1, const CardList &c2) const
+{
+ int fcs, fss;
+ countFreeCells(fcs, fss);
+
+ if (c1->isEmpty()) // destination is empty
+ fss--;
+
+ if (!unlimited_move && int(c2.count()) > ((fcs)+1)<<fss)
+ return false;
+
+ // ok if the target is empty
+ if (c1->isEmpty())
+ return true;
+
+ Card *c = c2.first(); // we assume there are only valid sequences
+
+ // ok if in sequence, alternate colors
+ return ((c1->top()->rank() == (c->rank()+1))
+ && (c1->top()->isRed() != c->isRed()));
+}
+
+bool FreecellBase::checkAdd(int, const Pile *c1, const CardList &c2) const
+{
+ return CanPutStore(c1, c2);
+}
+
+//-------------------------------------------------------------------------//
+
+bool FreecellBase::checkRemove(int checkIndex, const Pile *p, const Card *c) const
+{
+ if (checkIndex != 0)
+ return false;
+
+ // ok if just one card
+ if (c == p->top())
+ return true;
+
+ // Now we're trying to move two or more cards.
+
+ // First, let's check if the column is in valid
+ // (that is, in sequence, alternated colors).
+ int index = p->indexOf(c) + 1;
+ const Card *before = c;
+ while (true)
+ {
+ c = p->at(index++);
+
+ if (!((c->rank() == (before->rank()-1))
+ && (c->isRed() != before->isRed())))
+ {
+ return false;
+ }
+ if (c == p->top())
+ return true;
+ before = c;
+ }
+
+ return true;
+}
+
+//-------------------------------------------------------------------------//
+
+class Freecell : public FreecellBase
+{
+public:
+ Freecell( KMainWindow* parent=0, const char* name=0);
+ virtual void deal();
+};
+
+Freecell::Freecell( KMainWindow* parent, const char* name)
+ : FreecellBase(1, 8, 4, FCS_ES_FILLED_BY_ANY_CARD, false, parent, name)
+{
+ for (int i = 0; i < 8; i++)
+ store[i]->move(8 + ( cardMap::CARDX() * 11 / 10 + 1 ) * i, 8 + cardMap::CARDY() * 11 / 10);
+
+ const int right = 8 + ( cardMap::CARDX() * 11 / 10 + 1 ) * 7 + cardMap::CARDX();
+
+ for (int i = 0; i < 4; i++)
+ freecell[i]->move(8 + ( cardMap::CARDX() * 13 / 12 ) * i, 8);
+
+ for (int i = 0; i < 4; i++)
+ target[i]->move(right - (3-i) * ( cardMap::CARDX() * 13 / 12 ) -cardMap::CARDX() , 8);
+}
+
+void Freecell::deal()
+{
+ int column = 0;
+ while (!deck->isEmpty())
+ {
+ store[column]->add (deck->nextCard(), false, true);
+ column = (column + 1) % 8;
+ }
+}
+
+static class LocalDealerInfo3 : public DealerInfo
+{
+public:
+ LocalDealerInfo3() : DealerInfo(I18N_NOOP("&Freecell"), 3) {}
+ virtual Dealer *createGame(KMainWindow *parent) { return new Freecell(parent); }
+} ldi8;
+
+//-------------------------------------------------------------------------//
+
+#include"freecell.moc"
diff --git a/kpat/freecell.h b/kpat/freecell.h
new file mode 100644
index 00000000..aba311b6
--- /dev/null
+++ b/kpat/freecell.h
@@ -0,0 +1,95 @@
+/*---------------------------------------------------------------------------
+
+ freecell.cpp implements a patience card game
+
+ Copyright (C) 1997 Rodolfo Borges
+ (C) 2000 Stephan Kulow
+
+ * 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.
+
+---------------------------------------------------------------------------*/
+
+#ifndef _FREECELL_H_
+#define _FREECELL_H_
+
+#include "dealer.h"
+
+class FreecellPile : public Pile
+{
+public:
+ FreecellPile(int _index, Dealer* parent = 0) : Pile(_index, parent) {}
+ virtual void moveCards(CardList &c, Pile *to);
+};
+
+class FreecellBase : public Dealer
+{
+ Q_OBJECT
+
+public:
+ FreecellBase( int decks, int stores, int freecells, int es_filling, bool unlimited_move,
+ KMainWindow* parent=0, const char* name=0);
+ void moveCards(CardList &c, FreecellPile *from, Pile *to);
+ QString solverFormat() const;
+ virtual ~FreecellBase();
+
+public slots:
+ virtual void deal() = 0;
+ virtual void restart();
+ void waitForMoving(Card *c);
+ void startMoving();
+ void resumeSolution();
+ virtual void demo();
+
+protected:
+ virtual bool checkRemove( int checkIndex, const Pile *c1, const Card *c) const;
+ virtual bool checkAdd ( int checkIndex, const Pile *c1, const CardList& c2) const;
+
+ bool CanPutStore(const Pile *c1, const CardList& c2) const;
+ bool CanRemove(const Pile *c1, const Card *c) const;
+
+ void countFreeCells(int &free_cells, int &free_stores) const;
+
+ virtual void getHints();
+ void movePileToPile(CardList &c, Pile *to, PileList fss, PileList &fcs,
+ uint start, uint count, int debug_level);
+
+ Pile *pileForName(QString line) const;
+ void findSolution();
+
+ virtual MoveHint *chooseHint();
+ MoveHint *translateMove(void *m);
+ void freeSolution();
+
+ virtual void stopDemo();
+ virtual void newDemoMove(Card *m);
+ virtual bool cardDblClicked(Card *c);
+
+protected:
+ QValueList<FreecellPile*> store;
+ PileList freecell;
+ PileList target;
+ Deck *deck;
+private:
+ HintList moves;
+ HintList oldmoves;
+ Card *waitfor;
+ void *solver_instance;
+ int es_filling;
+ int solver_ret;
+ bool unlimited_move;
+ bool noLongerNeeded(const Card &);
+};
+
+#endif
+
+//-------------------------------------------------------------------------//
diff --git a/kpat/gamestats.ui b/kpat/gamestats.ui
new file mode 100644
index 00000000..2d1e5443
--- /dev/null
+++ b/kpat/gamestats.ui
@@ -0,0 +1,272 @@
+<!DOCTYPE UI><UI version="3.2" stdsetdef="1">
+<class>GameStats</class>
+<widget class="QDialog">
+ <property name="name">
+ <cstring>GameStats</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>310</width>
+ <height>211</height>
+ </rect>
+ </property>
+ <property name="caption">
+ <string>Statistics</string>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>true</bool>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout3</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Game:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox">
+ <property name="name">
+ <cstring>GameType</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>0</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout5</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>layout2</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLabel" row="1" column="1">
+ <property name="name">
+ <cstring>Won</cstring>
+ </property>
+ <property name="text">
+ <string>%1</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>WonPerc</cstring>
+ </property>
+ <property name="text">
+ <string>(%1%)</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel7</cstring>
+ </property>
+ <property name="text">
+ <string>Longest winning streak:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Games played:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="1">
+ <property name="name">
+ <cstring>LooseStreak</cstring>
+ </property>
+ <property name="text">
+ <string>%1</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="3" column="0">
+ <property name="name">
+ <cstring>textLabel8</cstring>
+ </property>
+ <property name="text">
+ <string>Longest losing streak:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="2" column="1">
+ <property name="name">
+ <cstring>WinStreak</cstring>
+ </property>
+ <property name="text">
+ <string>%1</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Games won:</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="1">
+ <property name="name">
+ <cstring>Played</cstring>
+ </property>
+ <property name="text">
+ <string>%1</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignRight</set>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>250</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </hbox>
+ </widget>
+ <spacer>
+ <property name="name">
+ <cstring>spacer3</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>71</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>Layout1</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <spacer>
+ <property name="name">
+ <cstring>Horizontal Spacing2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QPushButton">
+ <property name="name">
+ <cstring>buttonOk</cstring>
+ </property>
+ <property name="text">
+ <string>&amp;OK</string>
+ </property>
+ <property name="accel">
+ <string></string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+</widget>
+<connections>
+ <connection>
+ <sender>buttonOk</sender>
+ <signal>clicked()</signal>
+ <receiver>GameStats</receiver>
+ <slot>accept()</slot>
+ </connection>
+ <connection>
+ <sender>GameType</sender>
+ <signal>activated(int)</signal>
+ <receiver>GameStats</receiver>
+ <slot>setGameType(int)</slot>
+ </connection>
+</connections>
+<slots>
+ <slot>setGameType(int)</slot>
+</slots>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kpat/gamestatsimpl.cpp b/kpat/gamestatsimpl.cpp
new file mode 100644
index 00000000..a3a67a43
--- /dev/null
+++ b/kpat/gamestatsimpl.cpp
@@ -0,0 +1,57 @@
+#include "gamestatsimpl.h"
+#include "dealer.h"
+#include "version.h"
+
+#include <qcombobox.h>
+#include <qlabel.h>
+
+#include <kapplication.h>
+#include <kconfig.h>
+#include <klocale.h>
+
+GameStatsImpl::GameStatsImpl(QWidget* aParent, const char* aname)
+ : GameStats(aParent, aname)
+{
+ QStringList list;
+ QValueList<DealerInfo*>::ConstIterator it;
+ for (it = DealerInfoList::self()->games().begin();
+ it != DealerInfoList::self()->games().end(); ++it)
+ {
+ // while we develop, it may happen that some lower
+ // indices do not exist
+ uint index = (*it)->gameindex;
+ for (uint i = 0; i <= index; i++)
+ if (list.count() <= i)
+ list.append("unknown");
+ list[index] = i18n((*it)->name);
+ list[index].replace('&',"");
+ }
+ GameType->insertStringList(list);
+ showGameType(0);
+}
+
+void GameStatsImpl::showGameType(int id)
+{
+ GameType->setCurrentItem(id);
+ setGameType(id);
+}
+
+void GameStatsImpl::setGameType(int id)
+{
+ // Trick to reset string to original value
+ languageChange();
+ KConfig *config = kapp->config();
+ KConfigGroupSaver kcs(config, scores_group);
+ unsigned int t = config->readUnsignedNumEntry(QString("total%1").arg(id),0);
+ Played->setText(Played->text().arg(t));
+ unsigned int w = config->readUnsignedNumEntry(QString("won%1").arg(id),0);
+ Won->setText(Won->text().arg(w));
+ if (t)
+ WonPerc->setText(WonPerc->text().arg(w*100/t));
+ else
+ WonPerc->setText(WonPerc->text().arg(0));
+ WinStreak->setText(
+ WinStreak->text().arg(config->readUnsignedNumEntry(QString("maxwinstreak%1").arg(id),0)));
+ LooseStreak->setText(
+ LooseStreak->text().arg(config->readUnsignedNumEntry(QString("maxloosestreak%1").arg(id),0)));
+}
diff --git a/kpat/gamestatsimpl.h b/kpat/gamestatsimpl.h
new file mode 100644
index 00000000..192b7793
--- /dev/null
+++ b/kpat/gamestatsimpl.h
@@ -0,0 +1,16 @@
+#ifndef GAMESTATS_IMPL_H_
+#define GAMESTATS_IMPL_H_
+
+#include "gamestats.h"
+
+class GameStatsImpl : public GameStats
+{
+ public:
+ GameStatsImpl(QWidget* aParent, const char* aname);
+
+ virtual void setGameType(int i);
+ virtual void showGameType(int i);
+};
+
+#endif
+
diff --git a/kpat/golf.cpp b/kpat/golf.cpp
new file mode 100644
index 00000000..71bdfbb5
--- /dev/null
+++ b/kpat/golf.cpp
@@ -0,0 +1,169 @@
+#include "golf.h"
+#include <klocale.h>
+#include "deck.h"
+#include <kdebug.h>
+#include "cardmaps.h"
+
+HorRightPile::HorRightPile( int _index, Dealer* parent)
+ : Pile(_index, parent)
+{
+}
+
+QSize HorRightPile::cardOffset( bool _spread, bool, const Card *) const
+{
+ if (_spread)
+ return QSize(+hspread(), 0);
+
+ return QSize(0, 0);
+}
+
+//-------------------------------------------------------------------------//
+
+Golf::Golf( KMainWindow* parent, const char* _name)
+ : Dealer( parent, _name )
+{
+ const int dist_x = cardMap::CARDX() * 11 / 10 + 1;
+ const int pile_dist = 10 + 3 * cardMap::CARDY();
+
+ deck = Deck::new_deck( this);
+ deck->move(10, pile_dist);
+ connect(deck, SIGNAL(clicked(Card*)), SLOT(deckClicked(Card*)));
+
+ for( int r = 0; r < 7; r++ ) {
+ stack[r]=new Pile(1+r, this);
+ stack[r]->move(10+r*dist_x,10);
+ stack[r]->setAddFlags( Pile::addSpread | Pile::disallow);
+ stack[r]->setCheckIndex( 1 );
+ }
+
+ waste=new HorRightPile(8,this);
+ waste->move(10 + cardMap::CARDX() * 5 / 4, pile_dist);
+ waste->setTarget(true);
+ waste->setCheckIndex( 0 );
+ waste->setAddFlags( Pile::addSpread);
+
+ setActions(Dealer::Hint | Dealer::Demo);
+}
+
+//-------------------------------------------------------------------------//
+
+bool Golf::checkAdd( int checkIndex, const Pile *c1, const CardList& cl) const
+{
+ if (checkIndex == 1)
+ return false;
+
+ Card *c2 = cl.first();
+
+ kdDebug(11111)<<"check add "<< c1->name()<<" " << c2->name() <<" "<<endl;
+
+ if ((c2->rank() != (c1->top()->rank()+1)) && (c2->rank() != (c1->top()->rank()-1)))
+ return false;
+
+ return true;
+}
+
+bool Golf::checkRemove( int checkIndex, const Pile *, const Card *c2) const
+{
+ if (checkIndex == 0)
+ return false;
+ return (c2 == c2->source()->top());
+}
+
+//-------------------------------------------------------------------------//
+
+void Golf::restart()
+{
+ deck->collectAndShuffle();
+ deal();
+}
+
+void Golf::deckClicked(Card *)
+{
+ if (deck->isEmpty()) {
+ return;
+
+ }
+ Card *c = deck->nextCard();
+ waste->add(c, true, true);
+ int x = int(c->x());
+ int y = int(c->y());
+ c->move(deck->x(), deck->y());
+ c->flipTo(x, y, 8);
+}
+
+//-------------------------------------------------------------------------//
+
+void Golf::deal()
+{
+ for(int r=0;r<7;r++)
+ {
+ for(int i=0;i<5;i++)
+ {
+ stack[r]->add(deck->nextCard(),false,true);
+ }
+ }
+ waste->add(deck->nextCard(),false,false);
+
+}
+
+Card *Golf::demoNewCards()
+{
+ deckClicked(0);
+ return waste->top();
+}
+
+bool Golf::cardClicked(Card *c)
+{
+ if (c->source()->checkIndex() !=1) {
+ return Dealer::cardClicked(c);
+ }
+
+ if (c != c->source()->top())
+ return false;
+
+ Pile*p=findTarget(c);
+ if (p)
+ {
+ CardList empty;
+ empty.append(c);
+ c->source()->moveCards(empty, p);
+ canvas()->update();
+ return true;
+ }
+ return false;
+}
+
+bool Golf::isGameLost() const
+{
+ if( !deck->isEmpty())
+ return false;
+
+ bool onecard = false;
+
+ for( int r = 0; r < 7; r++ ) {
+ if( !stack[r]->isEmpty()){
+ onecard = true;
+ CardList stackTops;
+ stackTops.append(stack[r]->top());
+ if(this->checkAdd(0,waste,stackTops))
+ return false;
+ }
+ }
+
+ return onecard;
+}
+
+
+static class LocalDealerInfo13 : public DealerInfo
+{
+public:
+ LocalDealerInfo13() : DealerInfo(I18N_NOOP("Go&lf"), 12) {}
+ virtual Dealer *createGame(KMainWindow *parent) { return new Golf(parent); }
+} ldi13;
+
+//-------------------------------------------------------------------------//
+
+#include"golf.moc"
+
+//-------------------------------------------------------------------------//
+
diff --git a/kpat/golf.h b/kpat/golf.h
new file mode 100644
index 00000000..9c3b6421
--- /dev/null
+++ b/kpat/golf.h
@@ -0,0 +1,45 @@
+#ifndef _GOLF_H_
+#define _GOLF_H_
+
+#include "dealer.h"
+
+class HorRightPile : public Pile
+{
+ Q_OBJECT
+
+public:
+ HorRightPile( int _index, Dealer* parent = 0);
+ virtual QSize cardOffset( bool _spread, bool _facedown, const Card *before) const;
+};
+
+class Golf : public Dealer
+{
+ Q_OBJECT
+
+public:
+ Golf( KMainWindow* parent=0, const char* name=0);
+ void deal();
+ virtual void restart();
+ virtual bool isGameLost() const;
+
+protected slots:
+ void deckClicked(Card *);
+
+protected:
+ virtual bool startAutoDrop() { return false; }
+ virtual Card *demoNewCards();
+ virtual bool cardClicked(Card *c);
+
+private: // functions
+ virtual bool checkAdd( int checkIndex, const Pile *c1, const CardList& c2) const;
+ virtual bool checkRemove( int checkIndex, const Pile *c1, const Card *c2) const;
+
+private:
+ Pile* stack[7];
+ HorRightPile* waste;
+ Deck* deck;
+};
+
+#endif
+
+//-------------------------------------------------------------------------//
diff --git a/kpat/grandf.cpp b/kpat/grandf.cpp
new file mode 100644
index 00000000..9756b840
--- /dev/null
+++ b/kpat/grandf.cpp
@@ -0,0 +1,227 @@
+/***********************-*-C++-*-********
+
+ grandf.cpp implements a patience card game
+
+ Copyright (C) 1995 Paul Olav Tvete
+ (C) 2000 Stephan Kulow
+ * 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 "grandf.h"
+#include <klocale.h>
+#include "deck.h"
+#include <kaction.h>
+#include <assert.h>
+#include "cardmaps.h"
+
+Grandf::Grandf( KMainWindow* parent, const char *name )
+ : Dealer( parent, name )
+{
+ deck = Deck::new_deck(this);
+ deck->hide();
+
+ const int distx = cardMap::CARDX() * 14 / 10;
+
+ for (int i=0; i<4; i++) {
+ target[i] = new Pile(i+1, this);
+ target[i]->move(10+(i+1)*distx, 10);
+ target[i]->setType(Pile::KlondikeTarget);
+ }
+
+ for (int i=0; i<7; i++) {
+ store[i] = new Pile(5+i, this);
+ store[i]->move(10+distx*i, 10 + cardMap::CARDY() * 15 / 10);
+ store[i]->setAddFlags(Pile::addSpread | Pile::several);
+ store[i]->setRemoveFlags(Pile::several | Pile::autoTurnTop);
+ store[i]->setCheckIndex(1);
+ }
+
+ setActions(Dealer::Hint | Dealer::Demo | Dealer::Redeal);
+}
+
+void Grandf::restart() {
+ deck->collectAndShuffle();
+ deal();
+ numberOfDeals = 1;
+}
+
+void Grandf::redeal() {
+ unmarkAll();
+
+ if (numberOfDeals < 3) {
+ collect();
+ deal();
+ numberOfDeals++;
+ }
+ if (numberOfDeals == 3) {
+ aredeal->setEnabled(false);
+ }
+ takeState();
+}
+
+Card *Grandf::demoNewCards()
+{
+ if (numberOfDeals < 3) {
+ redeal();
+ return store[3]->top();
+ } else
+ return 0;
+}
+
+void Grandf::deal() {
+ int start = 0;
+ int stop = 7-1;
+ int dir = 1;
+
+ for (int round=0; round < 7; round++)
+ {
+ int i = start;
+ do
+ {
+ Card *next = deck->nextCard();
+ if (next)
+ store[i]->add(next, i != start, true);
+ i += dir;
+ } while ( i != stop + dir);
+ int t = start;
+ start = stop;
+ stop = t+dir;
+ dir = -dir;
+ }
+
+ int i = 0;
+ Card *next = deck->nextCard();
+ while (next)
+ {
+ store[i+1]->add(next, false , true);
+ next = deck->nextCard();
+ i = (i+1)%6;
+ }
+
+ for (int round=0; round < 7; round++)
+ {
+ Card *c = store[round]->top();
+ if (c)
+ c->turn(true);
+ }
+ aredeal->setEnabled(true);
+ canvas()->update();
+}
+
+/*****************************
+
+ Does the collecting step of the game
+
+ NOTE: this is not quite correct -- the piles should be turned
+ facedown (ie partially reversed) during collection.
+
+******************************/
+void Grandf::collect() {
+ unmarkAll();
+
+ for (int pos = 6; pos >= 0; pos--) {
+ CardList p = store[pos]->cards();
+ for (CardList::ConstIterator it = p.begin(); it != p.end(); ++it)
+ deck->add(*it, true, false);
+ }
+}
+
+bool Grandf::checkAdd( int checkIndex, const Pile *c1, const CardList& c2) const {
+ assert (checkIndex == 1);
+ if (c1->isEmpty())
+ return c2.first()->rank() == Card::King;
+ else
+ return (c2.first()->rank() == c1->top()->rank() - 1)
+ && c2.first()->suit() == c1->top()->suit();
+}
+
+QString Grandf::getGameState() const
+{
+ return QString::number(numberOfDeals);
+}
+
+void Grandf::setGameState( const QString &s)
+{
+ numberOfDeals = s.toInt();
+ aredeal->setEnabled(numberOfDeals < 3);
+}
+
+bool Grandf::isGameLost() const
+{
+ // If we can redeal, then nothing's lost yet.
+ if (numberOfDeals <3)
+ return false;
+
+ // Work through the stores, look for killer criteria.
+ for(int i=0; i < 7; i++) {
+
+ /* If this store is empty, then iterate through the other stores and
+ * check if there is a (visible) King card. If so, then we could move
+ * that to the free store (which means a turn is possible, so the
+ * game is not lost yet).
+ */
+ if(store[i]->isEmpty()){
+ for(int i2=1; i2 < 7; i2++) {
+ int j=(i+i2) % 7;
+ CardList p = store[j]->cards();
+ for (CardList::ConstIterator it = p.begin(); it != p.end(); ++it){
+ Card *c= *it;
+ if( it != p.begin() && c->realFace() && c->rank() == Card::King)
+ return false;
+ }
+ }
+ }
+ else{
+ /* If this store has an Ace as it's top card, then we can start a
+ * new target pile!
+ */
+ if(store[i]->top()->rank() == Card::Ace)
+ return false;
+
+ /* Check whether the top card of this store could be added to
+ * any of the target piles.
+ */
+ for(int j=0; j <4; j++)
+ if( !target[j]->isEmpty())
+ if(store[i]->top()->suit() == target[j]->top()->suit())
+ if( store[i]->top()->rank() == target[j]->top()->rank() +1)
+ return false;
+
+ /* Check whether any (group of) cards from another store could
+ * be put onto this store's top card.
+ */
+ for(int i2=1; i2 < 7; i2++) {
+ int j=(i+i2) % 7;
+ CardList p = store[j]->cards();
+ for (CardList::ConstIterator it = p.begin(); it != p.end(); ++it){
+ Card *c= *it;
+ if( c->realFace() &&
+ c->rank() == (store[i]->top()->rank()-1) &&
+ c->suit() == store[i]->top()->suit() )
+ return false;
+ }
+ }
+ }
+ }
+ return true; // can't move.
+}
+
+static class LocalDealerInfo1 : public DealerInfo
+{
+public:
+ LocalDealerInfo1() : DealerInfo(I18N_NOOP("&Grandfather"), 1) {}
+ virtual Dealer *createGame(KMainWindow *parent) { return new Grandf(parent); }
+} gfdi;
+
+#include "grandf.moc"
diff --git a/kpat/grandf.h b/kpat/grandf.h
new file mode 100644
index 00000000..db24483f
--- /dev/null
+++ b/kpat/grandf.h
@@ -0,0 +1,65 @@
+/***********************-*-C++-*-********
+
+ grandf.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.
+
+
+//
+// 7 positions, all cards on table, follow suit
+// ( I don't know a name for this one, but I learned it from my grandfather.)
+
+****************************************/
+
+
+#ifndef P_GRANDF_7
+#define P_GRANDF_7
+
+#include "dealer.h"
+
+class KAction;
+class Pile;
+class Deck;
+class KMainWindow;
+
+class Grandf : public Dealer {
+ Q_OBJECT
+
+public:
+ Grandf( KMainWindow* parent=0, const char* name=0);
+
+public slots:
+ void redeal();
+ void deal();
+ virtual void restart();
+ virtual bool isGameLost() const;
+
+
+protected:
+ void collect();
+ virtual bool checkAdd ( int checkIndex, const Pile *c1, const CardList& c2) const;
+ virtual QString getGameState() const;
+ virtual void setGameState( const QString & stream );
+ virtual Card *demoNewCards();
+
+private:
+ Pile* store[7];
+ Pile* target[4];
+ Deck *deck;
+ int numberOfDeals;
+
+};
+
+#endif
diff --git a/kpat/green.png b/kpat/green.png
new file mode 100644
index 00000000..26793078
--- /dev/null
+++ b/kpat/green.png
Binary files differ
diff --git a/kpat/gypsy.cpp b/kpat/gypsy.cpp
new file mode 100644
index 00000000..db58f7cd
--- /dev/null
+++ b/kpat/gypsy.cpp
@@ -0,0 +1,117 @@
+#include "gypsy.h"
+#include <klocale.h>
+#include "deck.h"
+#include "cardmaps.h"
+
+Gypsy::Gypsy( KMainWindow* 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;
+
+ deck = Deck::new_deck(this, 2);
+ deck->move(10 + dist_x / 2 + 8*dist_x, 10 + 45 * cardMap::CARDY() / 10);
+
+ connect(deck, SIGNAL(clicked(Card*)), SLOT(slotClicked(Card *)));
+
+ for (int i=0; i<8; i++) {
+ target[i] = new Pile(i+1, this);
+ target[i]->move(10+dist_x*(8+(i/4)), 10 + (i%4)*dist_y);
+ target[i]->setAddType(Pile::KlondikeTarget);
+ }
+
+ for (int i=0; i<8; i++) {
+ store[i] = new Pile(9+i, this);
+ store[i]->move(10+dist_x*i, 10);
+ store[i]->setAddType(Pile::GypsyStore);
+ store[i]->setRemoveType(Pile::FreecellStore);
+ }
+
+ setActions(Dealer::Hint | Dealer::Demo);
+}
+
+void Gypsy::restart() {
+ deck->collectAndShuffle();
+ deal();
+}
+
+void Gypsy::dealRow(bool faceup) {
+ for (int round=0; round < 8; round++)
+ store[round]->add(deck->nextCard(), !faceup, true);
+}
+
+void Gypsy::deal() {
+ dealRow(false);
+ dealRow(false);
+ dealRow(true);
+ takeState();
+}
+
+Card *Gypsy::demoNewCards()
+{
+ if (deck->isEmpty())
+ return 0;
+ dealRow(true);
+ return store[0]->top();
+}
+
+bool Gypsy::isGameLost() const {
+ if(!deck->isEmpty())
+ return false;
+
+ for(int i=0; i < 8; i++){
+ if(store[i]->isEmpty())
+ return false;
+
+ if(store[i]->top()->rank() == Card::Ace)
+ return false;
+
+ for(int j=0; j <8; j++){
+ if(!target[j]->isEmpty() &&
+ (store[i]->top()->suit()==target[j]->top()->suit()) &&
+ (store[i]->top()->rank()==(target[j]->top()->rank()+1)))
+ return false;
+ }
+ }
+
+ for(int i=0; i < 8; i++) {
+ Card *cnext=store[i]->top();
+ int indexi=store[i]->indexOf(cnext);
+
+ Card *cardi= 0;
+ do{
+ cardi=cnext;
+ if (indexi>0)
+ cnext=store[i]->at( --indexi );
+
+ for(int k=0; k <8; k++) {
+ if (i == k)
+ continue;
+
+ if((cardi->rank()+1 == store[k]->top()->rank()) &&
+ cardi->isRed() != store[k]->top()->isRed()){
+
+ // this test doesn't apply if indexi==0, but fails gracefully.
+ if(cnext->rank() == store[k]->top()->rank() &&
+ cnext->suit() == store[k]->top()->suit())
+ break; //nothing gained; keep looking.
+
+ return false;// TODO: look deeper, move may not be helpful.
+ }
+ }
+
+ } while((indexi>=0) && (cardi->rank()+1 == cnext->rank()) &&
+ (cardi->isRed() != cnext->isRed()));
+ }
+
+ return true;
+}
+
+static class LocalDealerInfo7 : public DealerInfo
+{
+public:
+ LocalDealerInfo7() : DealerInfo(I18N_NOOP("Gy&psy"), 7) {}
+ virtual Dealer *createGame(KMainWindow *parent) { return new Gypsy(parent); }
+} gyfdi;
+
+#include "gypsy.moc"
diff --git a/kpat/gypsy.h b/kpat/gypsy.h
new file mode 100644
index 00000000..ea28b04e
--- /dev/null
+++ b/kpat/gypsy.h
@@ -0,0 +1,34 @@
+
+#ifndef GYPSY_H
+#define GYPSY_H
+
+#include "dealer.h"
+
+class KAction;
+class Pile;
+class Deck;
+class KMainWindow;
+
+class Gypsy : public Dealer {
+ Q_OBJECT
+
+public:
+ Gypsy( KMainWindow* parent=0, const char* name=0);
+ virtual bool isGameLost() const;
+
+public slots:
+ void slotClicked(Card *) { dealRow(true); }
+ void deal();
+ virtual void restart();
+
+private: // functions
+ void dealRow(bool faceup);
+ virtual Card *demoNewCards();
+
+private:
+ Pile* store[8];
+ Pile* target[8];
+ Deck *deck;
+};
+
+#endif
diff --git a/kpat/hint.h b/kpat/hint.h
new file mode 100644
index 00000000..1d1c2594
--- /dev/null
+++ b/kpat/hint.h
@@ -0,0 +1,28 @@
+#ifndef HINT_H
+#define HINT_H
+
+
+class Card;
+class Pile;
+
+
+class MoveHint
+{
+public:
+ MoveHint(Card *card, Pile *to, bool d=true);
+
+ bool dropIfTarget() const { return m_dropiftarget; }
+ Card *card() const { return m_card; }
+ Pile *pile() const { return m_to; }
+
+private:
+ Card *m_card;
+ Pile *m_to;
+ bool m_dropiftarget;
+};
+
+
+typedef QValueList<MoveHint*> HintList;
+
+
+#endif
diff --git a/kpat/icons/Makefile.am b/kpat/icons/Makefile.am
new file mode 100644
index 00000000..24597080
--- /dev/null
+++ b/kpat/icons/Makefile.am
@@ -0,0 +1,6 @@
+
+EXTRA_DIST = kpat-lq.png
+
+KDE_ICON = kpat
+
+
diff --git a/kpat/icons/hi128-app-kpat.png b/kpat/icons/hi128-app-kpat.png
new file mode 100644
index 00000000..5bb5cdc7
--- /dev/null
+++ b/kpat/icons/hi128-app-kpat.png
Binary files differ
diff --git a/kpat/icons/hi16-app-kpat.png b/kpat/icons/hi16-app-kpat.png
new file mode 100644
index 00000000..a33e861c
--- /dev/null
+++ b/kpat/icons/hi16-app-kpat.png
Binary files differ
diff --git a/kpat/icons/hi22-app-kpat.png b/kpat/icons/hi22-app-kpat.png
new file mode 100644
index 00000000..529d5de5
--- /dev/null
+++ b/kpat/icons/hi22-app-kpat.png
Binary files differ
diff --git a/kpat/icons/hi32-app-kpat.png b/kpat/icons/hi32-app-kpat.png
new file mode 100644
index 00000000..b5424928
--- /dev/null
+++ b/kpat/icons/hi32-app-kpat.png
Binary files differ
diff --git a/kpat/icons/hi48-app-kpat.png b/kpat/icons/hi48-app-kpat.png
new file mode 100644
index 00000000..41ea79b4
--- /dev/null
+++ b/kpat/icons/hi48-app-kpat.png
Binary files differ
diff --git a/kpat/icons/hi64-app-kpat.png b/kpat/icons/hi64-app-kpat.png
new file mode 100644
index 00000000..0ec12569
--- /dev/null
+++ b/kpat/icons/hi64-app-kpat.png
Binary files differ
diff --git a/kpat/icons/kpat-lq.png b/kpat/icons/kpat-lq.png
new file mode 100644
index 00000000..8ea647d2
--- /dev/null
+++ b/kpat/icons/kpat-lq.png
Binary files differ
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"
diff --git a/kpat/idiot.h b/kpat/idiot.h
new file mode 100644
index 00000000..101957c0
--- /dev/null
+++ b/kpat/idiot.h
@@ -0,0 +1,59 @@
+/***********************-*-C++-*-********
+
+ idiot.h 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.
+
+****************************************/
+
+
+#ifndef P_IDIOT
+#define P_IDIOT
+
+
+#include "dealer.h"
+
+
+class Idiot: public Dealer
+{
+ Q_OBJECT
+
+public:
+
+ Idiot( KMainWindow* parent = 0, const char* name = 0 );
+
+ virtual bool isGameWon() const;
+
+protected:
+ virtual bool cardClicked(Card *);
+ virtual bool cardDblClicked(Card *);
+ virtual void getHints();
+ virtual Card *demoNewCards();
+ virtual bool startAutoDrop() { return false; }
+
+public slots:
+
+ virtual void restart();
+ void deal();
+
+private:
+ bool canMoveAway(Card *c);
+
+ Pile *m_play[ 4 ];
+ Pile *m_away;
+ Deck *m_deck;
+};
+
+#endif
diff --git a/kpat/kings.cpp b/kpat/kings.cpp
new file mode 100644
index 00000000..7654a88f
--- /dev/null
+++ b/kpat/kings.cpp
@@ -0,0 +1,132 @@
+#include "kings.h"
+#include <klocale.h>
+#include <kdebug.h>
+#include "deck.h"
+#include <assert.h>
+#include "freecell-solver/fcs_enums.h"
+#include "cardmaps.h"
+
+Kings::Kings( KMainWindow* parent, const char *name )
+ : FreecellBase( 2, 8, 8, FCS_ES_FILLED_BY_KINGS_ONLY, true, parent, name )
+{
+ const int dist_x = cardMap::CARDX() * 11 / 10 + 1;
+
+ for (int i=0; i<8; i++) {
+ target[i]->move((8 + i/4) * dist_x + 10 + cardMap::CARDX() * 4 / 10, 10 + (i % 4) * cardMap::CARDY() * 14 / 10 );
+ store[i]->move(10+dist_x*i, 10 + cardMap::CARDY() * 5 / 4);
+ store[i]->setSpread(13);
+ freecell[i]->move(10 + dist_x * i, 10);
+ }
+}
+
+void Kings::demo()
+{
+ Dealer::demo();
+}
+
+void Kings::deal() {
+ CardList cards = deck->cards();
+ CardList::Iterator it = cards.begin();
+ int cn = 0;
+ for (int stack = -1; stack < 8; )
+ {
+ while (it != cards.end() && (*it)->rank() != Card::King) {
+ if (stack >= 0) {
+ store[stack]->add(*it, false, true);
+ cn++;
+ }
+ ++it;
+ }
+ if (it == cards.end())
+ break;
+ cn++;
+ store[++stack]->add(*it, false, true);
+ if (stack == 0) {
+ cards = deck->cards(); // reset to start
+ it = cards.begin();
+ } else
+ ++it;
+ }
+ assert(cn == 104);
+}
+
+bool Kings::isGameLost() const {
+ int i,indexi;
+ Card *c,*cnext,*ctarget;
+ CardList targets,ctops;
+
+ for(i=0; i < 8; i++){
+ if(freecell[i]->isEmpty())
+ return false;
+ if(store[i]->isEmpty())
+ return false;
+ if(store[i]->top()->rank() == Card::Ace)
+ return false;
+ }
+
+ for(i=0; i < 8; i++){
+ if(!target[i]->isEmpty())
+ targets.append(target[i]->top());
+
+ if(!store[i]->isEmpty())
+ ctops.append(store[i]->top());
+ }
+
+ for(i=0; i < 8; i++){
+ if(store[i]->isEmpty())
+ continue;
+
+ c=store[i]->top();
+ for (CardList::Iterator it = targets.begin(); it != targets.end(); ++it) {
+ ctarget=*it;
+ if(c->rank()-1 == ctarget->rank() &&
+ c->suit() == ctarget->suit()){
+ kdDebug(11111)<< "test 1" << endl;
+ return false;
+ }
+ }
+
+ for(indexi=store[i]->indexOf(store[i]->top()); indexi>=0;indexi--){
+ c=store[i]->at(indexi);
+ if(indexi > 0)
+ cnext=store[i]->at(indexi-1);
+
+ for (CardList::Iterator it = ctops.begin(); it != ctops.end(); ++it) {
+ ctarget=*it;
+ if(c->rank()+1 == ctarget->rank() &&
+ c->isRed() != ctarget->isRed()){
+
+ if(indexi == 0)
+ return false;
+
+ if(cnext->rank() != ctarget->rank()
+ || cnext->suit() != ctarget->suit())
+ return false;
+ }
+ }
+ if(cnext->rank() != c->rank()+1 &&
+ cnext->isRed() != c->isRed())
+ break;
+ }
+ }
+
+ return true;
+}
+
+#if 0
+NOTE: When this is reenabled, renumber the following patiences back again:
+Golf
+Klondike, draw 3
+Spider Easy
+Spider Medium
+Spider Hard
+
+static class LocalDealerInfo12 : public DealerInfo
+{
+public:
+ LocalDealerInfo12() : DealerInfo(I18N_NOOP("&The Kings"), 12) {}
+ virtual Dealer *createGame(KMainWindow *parent) { return new Kings(parent); }
+} gfdi12;
+#endif
+
+#include "kings.moc"
diff --git a/kpat/kings.h b/kpat/kings.h
new file mode 100644
index 00000000..e3a60475
--- /dev/null
+++ b/kpat/kings.h
@@ -0,0 +1,22 @@
+#ifndef _KINGS_H_
+#define _KINGS_H_
+
+#include "freecell.h"
+
+class Pile;
+class Deck;
+class KMainWindow;
+
+class Kings : public FreecellBase {
+ Q_OBJECT
+
+public:
+ Kings( KMainWindow* parent=0, const char* name=0);
+ virtual bool isGameLost() const;
+
+public slots:
+ virtual void deal();
+ virtual void demo();
+};
+
+#endif
diff --git a/kpat/klondike.cpp b/kpat/klondike.cpp
new file mode 100644
index 00000000..ff561fa3
--- /dev/null
+++ b/kpat/klondike.cpp
@@ -0,0 +1,495 @@
+/***********************-*-C++-*-********
+
+ klondike.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.
+
+//
+// 7 positions, alternating red and black
+//
+
+****************************************/
+
+#include "klondike.h"
+#include <klocale.h>
+#include "deck.h"
+#include <kdebug.h>
+#include <assert.h>
+#include "cardmaps.h"
+
+class KlondikePile : public Pile
+{
+public:
+ KlondikePile( int _index, Dealer* parent)
+ : Pile(_index, parent) {}
+
+ void clearSpread() { cardlist.clear(); }
+
+ void addSpread(Card *c) {
+ cardlist.append(c);
+ }
+ virtual QSize cardOffset( bool _spread, bool, const Card *c) const {
+ kdDebug(11111) << "cardOffset " << _spread << " " << (c? c->name() : "(null)") << endl;
+ if (cardlist.contains(const_cast<Card * const>(c)))
+ return QSize(+dspread(), 0);
+ return QSize(0, 0);
+ }
+private:
+ CardList cardlist;
+};
+
+Klondike::Klondike( bool easy, KMainWindow* parent, const char* _name )
+ : Dealer( parent, _name )
+{
+ // The units of the follwoing constants are pixels
+ const int margin = 10; // between card piles and board edge
+ const int hspacing = cardMap::CARDX() / 6 + 1; // horizontal spacing between card piles
+ const int vspacing = cardMap::CARDY() / 4; // vertical spacing between card piles
+
+ deck = Deck::new_deck(this);
+ deck->move(margin, margin);
+
+ EasyRules = easy;
+
+ pile = new KlondikePile( 13, this);
+
+ pile->move(margin + cardMap::CARDX() + cardMap::CARDX() / 4, margin);
+ // Move the visual representation of the pile to the intended position
+ // on the game board.
+
+ pile->setAddFlags(Pile::disallow);
+ pile->setRemoveFlags(Pile::Default);
+
+ for( int i = 0; i < 7; i++ ) {
+ play[ i ] = new Pile( i + 5, this);
+ play[i]->move(margin + (cardMap::CARDX() + hspacing) * i, margin + cardMap::CARDY() + vspacing);
+ play[i]->setAddType(Pile::KlondikeStore);
+ play[i]->setRemoveFlags(Pile::several | Pile::autoTurnTop | Pile::wholeColumn);
+ }
+
+ for( int i = 0; i < 4; i++ ) {
+ target[ i ] = new Pile( i + 1, this );
+ target[i]->move(margin + (3 + i) * (cardMap::CARDX()+ hspacing), margin);
+ target[i]->setAddType(Pile::KlondikeTarget);
+ if (EasyRules) // change default
+ target[i]->setRemoveFlags(Pile::Default);
+ else
+ target[i]->setRemoveType(Pile::KlondikeTarget);
+ }
+
+ setActions(Dealer::Hint | Dealer::Demo);
+
+ redealt = false;
+}
+
+// This function returns true when it is certain that the card t is no longer
+// needed on any of the play piles. This function is recursive but the
+// recursion will not get deep.
+//
+// To determine wether a card is no longer needed on any of the play piles we
+// obviously must know what a card can be used for there. According to the
+// rules a card can be used to store another card with 1 less unit of value
+// and opposite color. This is the only thing that a card can be used for
+// there. Therefore the cards with lowest value (1) are useless there (base
+// case). The other cards each have 2 cards that can be stored on them, let us
+// call those 2 cards *depending cards*.
+//
+// The object of the game is to put all cards on the target piles. Therefore
+// cards that are no longer needed on any of the play piles should be put on
+// the target piles if possible. Cards on the target piles can not be moved
+// and they can not store any of its depending cards. Let us call this that
+// the cards on the target piles are *out of play*.
+//
+// The simple and obvious rule is:
+// A card is no longer needed when both of its depending cards are out of
+// play.
+//
+// But using only the simplest rule to determine if a card is no longer
+// needed on any of the play piles is not ambitios enough. Therefore, if a
+// depending card is not out of play, we test if it could become out of play.
+// The requirement for getting a card out of play is that it can be placed on
+// a target pile and that it is no longer needed on any of the play piles
+// (this is why this function is recursive). This more ambitious rule lets
+// us extend the base case with the second lowest value (2).
+bool Klondike::noLongerNeeded(Card::Rank r, Card::Suit s) {
+
+ if (r <= Card::Two) return true; // Base case.
+
+ // Find the 2 suits of opposite color. "- 1" is used here because the
+ // siuts are ranged 1 .. 4 but target_tops is indexed 0 .. 3. (Of course
+ // the subtraction of 1 does not affect performance because it is a
+ // constant expression that is calculated at compile time).
+ unsigned char a = Card::Clubs - 1, b = Card::Spades - 1;
+ if (s == Card::Clubs || s == Card::Spades)
+ a = Card::Diamonds - 1, b = Card::Hearts - 1;
+
+ const Card::Rank depending_rank = static_cast<Card::Rank>(r - 1);
+ return
+ (((target_tops[a] >= depending_rank)
+ ||
+ ((target_tops[a] >= depending_rank - 1)
+ &&
+ (noLongerNeeded
+ (depending_rank, static_cast<Card::Suit>(a + 1)))))
+ &&
+ ((target_tops[b] >= depending_rank)
+ ||
+ ((target_tops[b] >= depending_rank - 1)
+ &&
+ (noLongerNeeded
+ (depending_rank, static_cast<Card::Suit>(b + 1))))));
+}
+
+bool Klondike::tryToDrop(Card *t)
+{
+ if (!t || !t->realFace() || t->takenDown())
+ return false;
+
+// kdDebug(11111) << "tryToDrop " << t->name() << endl;
+
+ Pile *tgt = findTarget(t);
+ if (tgt) {
+ newHint
+ (new MoveHint(t, tgt, noLongerNeeded(t->rank(), t->suit())));
+ return true;
+ }
+ return false;
+}
+
+void Klondike::getHints() {
+
+ target_tops[0] = target_tops[1] = target_tops[2] = target_tops[3]
+ = Card::None;
+
+ for( int i = 0; i < 4; i++ )
+ {
+ Card *c = target[i]->top();
+ if (!c) continue;
+ target_tops[c->suit() - 1] = c->rank();
+ }
+
+
+ Card* t[7];
+ for(int i=0; i<7;i++)
+ t[i] = play[i]->top();
+
+ for(int i=0; i<7; i++)
+ {
+ CardList list = play[i]->cards();
+
+ for (CardList::ConstIterator it = list.begin(); it != list.end(); ++it)
+ {
+ if (!(*it)->isFaceUp())
+ continue;
+
+ CardList empty;
+ empty.append(*it);
+
+ for (int j = 0; j < 7; j++)
+ {
+ if (i == j)
+ continue;
+
+ if (play[j]->legalAdd(empty)) {
+ if (((*it)->rank() != Card::King) || it != list.begin()) {
+ newHint(new MoveHint(*it, play[j]));
+ break;
+ }
+ }
+ }
+ break; // the first face up
+ }
+
+ tryToDrop(play[i]->top());
+ }
+ if (!pile->isEmpty())
+ {
+ Card *t = pile->top();
+ if (!tryToDrop(t))
+ {
+ for (int j = 0; j < 7; j++)
+ {
+ CardList empty;
+ empty.append(t);
+ if (play[j]->legalAdd(empty)) {
+ newHint(new MoveHint(t, play[j]));
+ break;
+ }
+ }
+ }
+ }
+}
+
+Card *Klondike::demoNewCards() {
+ deal3();
+ if (!deck->isEmpty() && pile->isEmpty())
+ deal3(); // again
+ return pile->top();
+}
+
+void Klondike::restart() {
+ kdDebug(11111) << "restart\n";
+ deck->collectAndShuffle();
+ redealt = false;
+ deal();
+}
+
+void Klondike::deal3()
+{
+ int draw;
+
+ if ( EasyRules ) {
+ draw = 1;
+ } else {
+ draw = 3;
+ }
+
+ pile->clearSpread();
+
+ if (deck->isEmpty())
+ {
+ redeal();
+ return;
+ }
+
+ // move the cards back on the deck, so we can have three new
+ for (int i = 0; i < pile->cardsLeft(); ++i) {
+ pile->at(i)->move(pile->x(), pile->y());
+ }
+
+ for (int flipped = 0; flipped < draw ; ++flipped) {
+
+ Card *item = deck->nextCard();
+ if (!item) {
+ kdDebug(11111) << "deck empty!!!\n";
+ return;
+ }
+ pile->add(item, true, true); // facedown, nospread
+ if (flipped < draw - 1)
+ pile->addSpread(item);
+ // move back to flip
+ item->move(deck->x(), deck->y());
+
+ item->flipTo( int(pile->x()) + pile->dspread() * (flipped), int(pile->y()), 8 * (flipped + 1) );
+ }
+
+}
+
+// Add cards from pile to deck, in reverse direction
+void Klondike::redeal() {
+
+ CardList pilecards = pile->cards();
+ if (EasyRules)
+ // the remaining cards in deck should be on top
+ // of the new deck
+ pilecards += deck->cards();
+
+ for (int count = pilecards.count() - 1; count >= 0; --count)
+ {
+ Card *card = pilecards[count];
+ card->setAnimated(false);
+ deck->add(card, true, false); // facedown, nospread
+ }
+
+ redealt = true;
+}
+
+void Klondike::deal() {
+ for(int round=0; round < 7; round++)
+ for (int i = round; i < 7; i++ )
+ play[i]->add(deck->nextCard(), i != round, true);
+}
+
+bool Klondike::cardClicked(Card *c) {
+ kdDebug(11111) << "card clicked " << c->name() << endl;
+
+ if (Dealer::cardClicked(c))
+ return true;
+
+ if (c->source() == deck) {
+ pileClicked(deck);
+ return true;
+ }
+
+ return false;
+}
+
+void Klondike::pileClicked(Pile *c) {
+ kdDebug(11111) << "pile clicked " << endl;
+ Dealer::pileClicked(c);
+
+ if (c == deck) {
+ deal3();
+ }
+}
+
+bool Klondike::startAutoDrop()
+{
+ bool pileempty = pile->isEmpty();
+ if (!Dealer::startAutoDrop())
+ return false;
+ if (pile->isEmpty() && !pileempty)
+ deal3();
+ return true;
+}
+
+
+bool Klondike::isGameLost() const
+{
+ kdDebug( 11111 ) << "Is the game lost?" << endl;
+
+ if (!deck->isEmpty()) {
+ kdDebug( 11111 ) << "We should only check this when the deck is exhausted." << endl;
+ return false;
+ }
+
+ // Check whether top of the pile can be added to any of the target piles.
+ if ( !pile->isEmpty() ) {
+ for ( int i = 0; i < 4; ++i ) {
+ if ( target[ i ]->isEmpty() ) {
+ continue;
+ }
+ if ( pile->top()->suit() == target[ i ]->top()->suit() &&
+ pile->top()->rank() - 1 == target[ i ]->top()->rank() ) {
+ kdDebug( 11111 ) << "No, the source pile's top card could be added to target pile " << i << endl;
+ return false;
+ }
+ }
+ }
+
+ // Create a card list - srcPileCards - that contains all accessible
+ // cards in the pile and the deck.
+ CardList srcPileCards;
+ if ( EasyRules ) {
+ srcPileCards = pile->cards();
+ } else {
+ /* In the draw3 mode, not every card in the source pile is
+ * accessible, but only every third one.
+ */
+ for ( unsigned int i = 2; i < pile->cards().count(); i += 3 ) {
+ kdDebug( 11111 ) << "Found card "<< pile->cards()[i]->name()<< endl;
+ srcPileCards += pile->cards()[ i ];
+ }
+ if ( !pile->cards().isEmpty() && pile->cards().count() % 3 != 0 ) {
+ kdDebug( 11111 ) << "Found last card "<< pile->cards()[pile->cards().count() - 1]->name()<< endl;
+ srcPileCards += pile->cards()[ pile->cards().count() - 1 ];
+ }
+ }
+
+ // Check all seven stores
+ for ( int i = 0; i < 7; ++i ) {
+
+ // If this store is empty...
+ if ( play[ i ]->isEmpty() ) {
+ // ...check whether the pile contains a king we could move here.
+ CardList::ConstIterator it = srcPileCards.begin();
+ CardList::ConstIterator end = srcPileCards.end();
+ for ( ; it != end; ++it ) {
+ if ( ( *it )->rank() == Card::King ) {
+ kdDebug( 11111 ) << "No, the pile contains a king which we could move onto store " << i << endl;
+ return false;
+ }
+ }
+
+ // ...check whether any of the other stores contains a (visible)
+ // king we could move here.
+ for ( int j = 0; j < 7; ++j ) {
+ if ( j == i || play[ j ]->isEmpty() ) {
+ continue;
+ }
+ const CardList cards = play[ j ]->cards();
+ CardList::ConstIterator it = ++cards.begin();
+ CardList::ConstIterator end = cards.end();
+ for ( ; it != end; ++it ) {
+ if ( ( *it )->realFace() && ( *it )->rank() == Card::King ) {
+ kdDebug( 11111 ) << "No, store " << j << " contains a visible king which we could move onto store " << i << endl;
+ return false;
+ }
+ }
+ }
+ } else { // This store is not empty...
+ Card *topCard = play[ i ]->top();
+
+ // ...check whether the top card is an Ace (we can start a target)
+ if ( topCard->rank() == Card::Ace ) {
+ kdDebug( 11111 ) << "No, store " << i << " has an Ace, we could start a target pile." << endl;
+ return false;
+ }
+
+ // ...check whether the top card can be added to any target pile
+ for ( int targetIdx = 0; targetIdx < 4; ++targetIdx ) {
+ if ( target[ targetIdx ]->isEmpty() ) {
+ continue;
+ }
+ if ( target[ targetIdx ]->top()->suit() == topCard->suit() &&
+ target[ targetIdx ]->top()->rank() == topCard->rank() - 1 ) {
+ kdDebug( 11111 ) << "No, store " << i << "'s top card could be added to target pile " << targetIdx << endl;
+ return false;
+ }
+ }
+
+ // ...check whether the source pile contains a card which can be
+ // put onto this store.
+ CardList::ConstIterator it = srcPileCards.begin();
+ CardList::ConstIterator end = srcPileCards.end();
+ for ( ; it != end; ++it ) {
+ if ( ( *it )->isRed() != topCard->isRed() &&
+ ( *it )->rank() == topCard->rank() - 1 ) {
+ kdDebug( 11111 ) << "No, the pile contains a card which we could add to store " << i << endl;
+ return false;
+ }
+ }
+
+ // ...check whether any of the other stores contains a visible card
+ // which can be put onto this store, and which is on top of an
+ // uncovered card.
+ for ( int j = 0; j < 7; ++j ) {
+ if ( j == i ) {
+ continue;
+ }
+ const CardList cards = play[ j ]->cards();
+ CardList::ConstIterator it = cards.begin();
+ CardList::ConstIterator end = cards.end();
+ for ( ; it != end; ++it ) {
+ if ( ( *it )->realFace() &&
+ ( *it )->isRed() != topCard->isRed() &&
+ ( *it )->rank() == topCard->rank() - 1 ) {
+ kdDebug( 11111 ) << "No, store " << j << " contains a card which we could add to store " << i << endl;
+ return false;
+ }
+ }
+ }
+ }
+ }
+ kdDebug( 11111 ) << "Yep, all hope is lost." << endl;
+ return true;
+}
+
+static class LocalDealerInfo0 : public DealerInfo
+{
+public:
+ LocalDealerInfo0() : DealerInfo(I18N_NOOP("&Klondike"), 0) {}
+ virtual Dealer *createGame(KMainWindow *parent) { return new Klondike(true, parent); }
+} ldi0;
+
+static class LocalDealerInfo14 : public DealerInfo
+{
+public:
+ LocalDealerInfo14() : DealerInfo(I18N_NOOP("Klondike (&draw 3)"), 13) {}
+ virtual Dealer *createGame(KMainWindow *parent) { return new Klondike(false, parent); }
+} ldi14;
+
+
+#include "klondike.moc"
diff --git a/kpat/klondike.h b/kpat/klondike.h
new file mode 100644
index 00000000..b1b7e673
--- /dev/null
+++ b/kpat/klondike.h
@@ -0,0 +1,71 @@
+/***********************-*-C++-*-********
+
+ klondike.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.
+
+
+//
+// 7 positions, alternating red and black
+//
+
+
+****************************************/
+
+#ifndef P_KLONDIKE
+#define P_KLONDIKE
+
+#include "dealer.h"
+
+class KlondikePile;
+
+class Klondike : public Dealer {
+ Q_OBJECT
+
+public:
+ Klondike( bool easy, KMainWindow* parent=0, const char* name=0);
+
+public:
+ virtual void restart();
+ virtual bool startAutoDrop();
+
+ void redeal(); // put pile back into deck
+ void deal();
+ void deal3(); // move up to 3 cards from deck to pile
+
+ virtual bool cardClicked(Card *);
+ virtual void pileClicked(Pile *c);
+ virtual void getHints();
+
+ virtual Card *demoNewCards();
+
+ bool tryToDrop(Card *t);
+
+ virtual bool isGameLost() const;
+
+private:
+ bool EasyRules;
+ bool redealt;
+
+ Pile* play[7];
+ Pile* target[4];
+
+ KlondikePile *pile;
+ Deck* deck;
+ Card::Rank target_tops[4];
+ bool noLongerNeeded(Card::Rank r, Card::Suit s );
+};
+
+#endif
diff --git a/kpat/kpat.desktop b/kpat/kpat.desktop
new file mode 100644
index 00000000..f1df018b
--- /dev/null
+++ b/kpat/kpat.desktop
@@ -0,0 +1,116 @@
+[Desktop Entry]
+Name=Patience
+Name[ar]=لعبة الصبر (Patience)
+Name[az]=Səbir
+Name[be]=Пасьянс
+Name[bn]=পেশেন্স
+Name[br]=Habaskter
+Name[bs]=Pasijans
+Name[ca]=Paciència
+Name[cs]=Passiáns
+Name[da]=Kabale
+Name[de]=Patiencen
+Name[el]=Πασιέντζα
+Name[eo]=Solitero
+Name[es]=Solitario
+Name[et]=Kaardimängud
+Name[eu]=Pazientzia
+Name[fi]=Pasianssi
+Name[fr]=Réussite
+Name[gl]=Solitario
+Name[hi]=पेशेन्स
+Name[hr]=Pasijans
+Name[hu]=Pasziánsz
+Name[is]=Kaplar
+Name[it]=Solitario
+Name[ja]=ソリティア
+Name[lt]=Atkaklumas
+Name[lv]=Pacietība
+Name[mk]=Пасијанс
+Name[mt]=Paċenzja
+Name[nb]=Kabal
+Name[ne]=धैर्य
+Name[nn]=Kabal
+Name[pa]=ਪੇਟੀਨਸ਼
+Name[pl]=Pasjans
+Name[pt]=Paciência
+Name[pt_BR]=Paciência
+Name[ro]=Pasenţe
+Name[se]=Kabála
+Name[sl]=Pasjansa
+Name[sv]=Patiens
+Name[ta]=பொறுமை
+Name[tg]=Пасянс
+Name[tr]=Sabır
+Name[uk]=Терпіння
+Name[ven]=Mukhondeleli
+Name[wa]=Pacyince
+Name[xh]= Nyamezela
+Name[zh_CN]=耐心
+Name[zh_TW]=Patience 耐心
+Name[zu]=Isineke
+Exec=kpat %i %m -caption "%c"
+Type=Application
+Icon=kpat
+DocPath=kpat/index.html
+GenericName=Patience Card Game
+GenericName[af]=Patience Kaart Speletjie
+GenericName[be]=Картачны пасьянс
+GenericName[bg]=Пасианс
+GenericName[bn]=পেশেন্স নামের তাস খেলা
+GenericName[br]=C'hoari a habaskter
+GenericName[bs]=Pasijans igra s kartama
+GenericName[ca]=Partida de cartes paciència
+GenericName[cs]=Karetní hra passiáns
+GenericName[cy]=Gêm Cerdiau Patience
+GenericName[da]=Kabale-kortspil
+GenericName[de]=Patiencen legen
+GenericName[el]=Παιχνίδι καρτών patience
+GenericName[eo]=Pacienca Kartludo
+GenericName[es]=Juego de cartas de solitario
+GenericName[et]=Kaardimäng
+GenericName[eu]=Pazientziako karta-jokoa
+GenericName[fa]=بازی Patience Card
+GenericName[fi]=Pasianssikorttipeli
+GenericName[fr]=Jeux de réussite
+GenericName[gl]=Solitario, xogo de cartas
+GenericName[he]=משחק קלפים
+GenericName[hi]=पेशेंस ताश का खेल
+GenericName[hr]=Kartaška igra pasijansa
+GenericName[hu]=Pasziánsz
+GenericName[is]=Kaplar
+GenericName[it]=Solitario
+GenericName[ja]=Patience カードゲーム
+GenericName[km]=ល្បែង​បៀ Patience
+GenericName[ko]=카드 놀이
+GenericName[lt]=Kantrybės kortų žaidimas
+GenericName[lv]=Pacietības kāršu spēle
+GenericName[mk]=Пасијанс - игра со карти
+GenericName[nb]=Kabalspill
+GenericName[nds]=Patiencen leggen
+GenericName[ne]=धैर्य कार्ड खेल
+GenericName[nl]=Patience-kaartspel
+GenericName[nn]=Kabalspel
+GenericName[pl]=Gra karciana Patience
+GenericName[pt]=Jogo de Paciência
+GenericName[pt_BR]=Jogo de Cartas Paciência
+GenericName[ro]=Joc de pasenţe
+GenericName[ru]=Пасьянс
+GenericName[se]=Kabálaspeallu
+GenericName[sk]=Kartová hra Patience
+GenericName[sl]=Igra s kartami Patience
+GenericName[sr]=Игра са картама Patience
+GenericName[sr@Latn]=Igra sa kartama Patience
+GenericName[sv]=Patienskortspel
+GenericName[ta]=பொறுமையான சீட்டு விளையாட்டு
+GenericName[tg]=Бозии Пасянси Кортӣ
+GenericName[tr]=Sabır Kart Oyunu
+GenericName[uk]=Гра в карти Терпіння
+GenericName[ven]=Mutambo wa Magarata wa Mukondeleli
+GenericName[wa]=Cwårdjeu d' pacyince
+GenericName[xh]=Ikhadi lomdlalo wokunyamezela
+GenericName[zh_CN]=考验耐心的牌类游戏
+GenericName[zh_TW]=耐心的紙牌遊戲
+GenericName[zu]=Umdlalo wesineke wamakhadi
+X-DCOP-ServiceType=Multi
+Categories=Qt;KDE;Game;CardGame;
diff --git a/kpat/kpatui.rc b/kpat/kpatui.rc
new file mode 100644
index 00000000..218d9c39
--- /dev/null
+++ b/kpat/kpatui.rc
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE kpartgui>
+<kpartgui name="standard_containers" version="12">
+<MenuBar>
+ <Menu name="game" noMerge="1"><text>&amp;Game</text>
+ <Action name="new_game"/>
+ <Action name="open"/>
+ <Action name="open_recent"/>
+ <Separator/>
+ <Action name="save"/>
+ <Separator/>
+ <Action name="choose_game"/>
+ <Action name="restart_game"/>
+ <Action name="game_stats"/>
+ <Separator/>
+ <ActionList name="game_actions"/>
+ <Separator/>
+ <Action name="game_exit"/>
+ </Menu>
+ <Menu name="edit" noMerge="1"><text>&amp;Edit</text>
+ <Action name="undo_move"/>
+ </Menu>
+ <Menu name="settings" noMerge="1"><text>&amp;Settings</text>
+ <Action name="game_type"/>
+ <Action name="backside"/>
+ <Action name="wallpaper"/>
+ <Action name="animation"/>
+ <Action name="enable_autodrop"/>
+ </Menu>
+</MenuBar>
+<ToolBar name="mainToolBar"><text>Main Toolbar</text>
+ <Action name="new_game"/>
+ <Action name="restart_game"/>
+ <Action name="undo_move"/>
+ <ActionList name="game_actions"/>
+ <Action name="help_game"/>
+ <Action name="game_exit"/>
+</ToolBar>
+<StatusBar/>
+</kpartgui>
diff --git a/kpat/main.cpp b/kpat/main.cpp
new file mode 100644
index 00000000..58ad8652
--- /dev/null
+++ b/kpat/main.cpp
@@ -0,0 +1,73 @@
+/*
+ patience -- main program
+ 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.
+
+ */
+
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kaboutdata.h>
+
+#include "version.h"
+#include "pwidget.h"
+
+static const char description[] = I18N_NOOP("KDE Patience Game");
+
+static KCmdLineOptions options[] =
+{
+ { "+file", I18N_NOOP("File to load"), 0 },
+ KCmdLineLastOption
+};
+
+int main( int argc, char **argv )
+{
+ KAboutData aboutData( "kpat", I18N_NOOP("KPatience"),
+ KPAT_VERSION, description, KAboutData::License_GPL,
+ "(c) 1995, Paul Olav Tvete\n"
+ "(c) 2000 Stephan Kulow");
+ aboutData.addAuthor("Paul Olav Tvete");
+ aboutData.addAuthor("Mario Weilguni",0,"mweilguni@kde.org");
+ aboutData.addAuthor("Matthias Ettrich",0,"ettrich@kde.org");
+ aboutData.addAuthor("Rodolfo Borges",I18N_NOOP("Some Game Types"),"barrett@9hells.org");
+ aboutData.addAuthor("Peter H. Ruegg",0,"kpat@incense.org");
+ aboutData.addAuthor("Michael Koch", I18N_NOOP("Bug fixes"), "koch@kde.org");
+ aboutData.addAuthor("Marcus Meissner", I18N_NOOP("Shuffle algorithm for game numbers"),
+ "mm@caldera.de");
+ aboutData.addAuthor("Shlomi Fish", I18N_NOOP("Freecell Solver"), "shlomif@vipe.technion.ac.il");
+ aboutData.addAuthor("Stephan Kulow", I18N_NOOP("Rewrite and current maintainer"),
+ "coolo@kde.org");
+ aboutData.addAuthor("Erik Sigra", I18N_NOOP("Improved Klondike"), "sigra@home.se");
+ aboutData.addAuthor("Josh Metzler", I18N_NOOP("Spider Implementation"), "joshdeb@metzlers.org");
+ aboutData.addAuthor("Maren Pakura", I18N_NOOP("Documentation"), "maren@kde.org");
+ aboutData.addAuthor("Inge Wallin", I18N_NOOP("Bug fixes"), "inge@lysator.liu.se");
+
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineArgs::addCmdLineOptions (options);
+ KCmdLineArgs* args = KCmdLineArgs::parsedArgs();
+
+ KApplication a;
+ KGlobal::locale()->insertCatalogue("libkdegames");
+
+ if (a.isRestored())
+ RESTORE(pWidget)
+ else {
+ pWidget *w = new pWidget;
+ if (args->count())
+ w->openGame(args->url(0));
+ a.setMainWidget(w);
+ w->show();
+ }
+ return a.exec();
+}
diff --git a/kpat/mod3.cpp b/kpat/mod3.cpp
new file mode 100644
index 00000000..c69aa8e4
--- /dev/null
+++ b/kpat/mod3.cpp
@@ -0,0 +1,312 @@
+/*---------------------------------------------------------------------------
+
+ 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( KMainWindow* 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, SIGNAL(clicked(Card*)), 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(KMainWindow *parent) { return new Mod3(parent); }
+} ldi5;
+
+//-------------------------------------------------------------------------//
+
+#include"mod3.moc"
+
+//-------------------------------------------------------------------------//
+
diff --git a/kpat/mod3.h b/kpat/mod3.h
new file mode 100644
index 00000000..17ff6aa2
--- /dev/null
+++ b/kpat/mod3.h
@@ -0,0 +1,62 @@
+/*---------------------------------------------------------------------------
+
+ 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.
+
+ (I don't know a name for this one, if you do, please tell me.)
+
+---------------------------------------------------------------------------*/
+
+#ifndef _MOD3_H_
+#define _MOD3_H_
+
+#include "dealer.h"
+
+class Mod3 : public Dealer
+{
+ Q_OBJECT
+
+public:
+ Mod3( KMainWindow* parent=0, const char* name=0);
+
+ void deal();
+
+ virtual void restart();
+ virtual bool isGameLost() const;
+ virtual bool startAutoDrop();
+
+public slots:
+ void deckClicked(Card *c);
+
+protected:
+ virtual Card *demoNewCards();
+
+private: // functions
+ virtual bool checkAdd( int checkIndex, const Pile *c1, const CardList& c2) const;
+ virtual bool checkPrefering( int checkIndex, const Pile *c1, const CardList& c2) const;
+
+ void dealRow(int row);
+
+private:
+ Deck *deck;
+
+ Pile *stack[4][8];
+ Pile *aces;
+};
+
+#endif
+
+//-------------------------------------------------------------------------//
diff --git a/kpat/napoleon.cpp b/kpat/napoleon.cpp
new file mode 100644
index 00000000..ffdf245c
--- /dev/null
+++ b/kpat/napoleon.cpp
@@ -0,0 +1,204 @@
+/*
+ napoleon.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.
+
+ */
+
+#include "napoleon.h"
+#include <klocale.h>
+#include "deck.h"
+#include "cardmaps.h"
+
+Napoleon::Napoleon( KMainWindow* parent, const char* _name )
+ : Dealer( parent, _name )
+{
+ deck = Deck::new_deck( this );
+ connect(deck, SIGNAL(clicked(Card *)), SLOT(deal1(Card*)));
+
+ pile = new Pile( 1, this );
+ pile->setAddFlags( Pile::disallow );
+
+ for (int i = 0; i < 4; i++)
+ {
+ store[i] = new Pile( 2 + i, this );
+ store[i]->setCheckIndex( 0 );
+ target[i] = new Pile( 6 + i, this);
+ target[i]->setRemoveFlags( Pile::disallow );
+ target[i]->setCheckIndex(2);
+ target[i]->setTarget(true);
+ }
+
+ const int dist_store = cardMap::CARDX() * 55 / 100;
+ const int dist_target = dist_store / 2;
+ const int centre_x = 10 + cardMap::CARDX() + dist_store;
+ const int centre_y = 10 + cardMap::CARDY() + dist_store;
+
+ deck->move( centre_x + cardMap::CARDX() * 47 / 10, centre_y + cardMap::CARDY() + dist_store);
+ pile->move( centre_x + cardMap::CARDX() * 33 / 10, centre_y + cardMap::CARDY() + dist_store);
+
+ centre = new Pile( 10, this );
+ centre->setRemoveFlags( Pile::disallow );
+ centre->setCheckIndex(1);
+ centre->setTarget(true);
+
+ store[0]->move( centre_x, centre_y - cardMap::CARDY() - dist_store );
+ store[1]->move( centre_x + cardMap::CARDX() + dist_store, centre_y);
+ store[2]->move( centre_x, centre_y + cardMap::CARDY() + dist_store );
+ store[3]->move( centre_x - cardMap::CARDX() - dist_store, centre_y);
+ target[0]->move( centre_x - cardMap::CARDX() - dist_target, centre_y - cardMap::CARDY() - dist_target );
+ target[1]->move( centre_x + cardMap::CARDX() + dist_target, centre_y - cardMap::CARDY() - dist_target);
+ target[2]->move( centre_x + cardMap::CARDX() + dist_target, centre_y + cardMap::CARDY() + dist_target);
+ target[3]->move( centre_x - cardMap::CARDX() - dist_target, centre_y + cardMap::CARDY() + dist_target);
+ centre->move(centre_x, centre_y);
+
+ setActions(Dealer::Hint | Dealer::Demo);
+}
+
+void Napoleon::restart() {
+ deck->collectAndShuffle();
+ deal();
+}
+
+bool Napoleon::CanPutTarget( const Pile* c1, const CardList& cl) const {
+ Card *c2 = cl.first();
+
+ if (c1->isEmpty())
+ return c2->rank() == Card::Seven;
+ else
+ return (c2->rank() == c1->top()->rank() + 1);
+}
+
+bool Napoleon::CanPutCentre( const Pile* c1, const CardList& cl) const {
+ Card *c2 = cl.first();
+
+ if (c1->isEmpty())
+ return c2->rank() == Card::Six;
+
+ if (c1->top()->rank() == Card::Ace)
+ return (c2->rank() == Card::Six);
+ else
+ return (c2->rank() == c1->top()->rank() - 1);
+}
+
+bool Napoleon::checkAdd( int checkIndex, const Pile *c1, const CardList& c2) const
+{
+ switch (checkIndex) {
+ case 0:
+ return c1->isEmpty();
+ case 1:
+ return CanPutCentre(c1, c2);
+ case 2:
+ return CanPutTarget(c1, c2);
+ default:
+ return false;
+ }
+}
+
+void Napoleon::deal() {
+ if (deck->isEmpty())
+ return;
+
+ for (int i=0; i<4; i++)
+ store[i]->add(deck->nextCard(), false, false);
+}
+
+void Napoleon::deal1(Card *) {
+ Card *c = deck->nextCard();
+ if (!c)
+ return;
+ pile->add(c, true, false);
+ c->move(deck->x(), deck->y());
+ c->flipTo(int(pile->x()), int(pile->y()), 8);
+}
+
+Card *Napoleon::demoNewCards()
+{
+ if (deck->isEmpty())
+ return 0;
+ deal1(0);
+ return pile->top();
+}
+
+void Napoleon::getHints() {
+ CardList cards;
+ for (int i = 0; i < 4; i++)
+ {
+ if (!store[i]->isEmpty())
+ cards.append(store[i]->top());
+ }
+ if (pile->top())
+ cards.append(pile->top());
+
+ for (CardList::Iterator it = cards.begin(); it != cards.end(); ++it) {
+ CardList empty;
+ empty.append(*it);
+ if (CanPutCentre(centre, empty)) {
+ newHint(new MoveHint(*it, centre));
+ continue;
+ }
+ for (int i = 0; i < 4; i++) {
+ if (CanPutTarget(target[i], empty)) {
+ newHint(new MoveHint(*it, target[i]));
+ break;
+ }
+ }
+ }
+ if (pile->isEmpty())
+ return;
+
+ for (int i = 0; i < 4; i++) {
+ if (store[i]->isEmpty()) {
+ newHint(new MoveHint(pile->top(), store[i]));
+ return;
+ }
+ }
+}
+
+bool Napoleon::isGameLost() const
+{
+ CardList cards;
+ for (int i = 0; i < 4; i++)
+ {
+ if (store[i]->isEmpty())
+ return false;
+ else
+ cards.append(store[i]->top());
+ }
+
+ if (pile->top())
+ cards.append(pile->top());
+
+ for (CardList::Iterator it = cards.begin(); it != cards.end(); ++it) {
+ CardList empty;
+ empty.append(*it);
+ if(CanPutCentre(centre,empty)) return false;
+ for(int i=0; i<4; i++)
+ if(CanPutTarget(target[i],empty)) return false;
+ }
+
+ return (deck->isEmpty());
+}
+
+
+
+static class LocalDealerInfo4 : public DealerInfo
+{
+public:
+ LocalDealerInfo4() : DealerInfo(I18N_NOOP("&Napoleon's Tomb"), 4) {}
+ virtual Dealer *createGame(KMainWindow *parent) { return new Napoleon(parent); }
+} ldi3;
+
+#include "napoleon.moc"
diff --git a/kpat/napoleon.h b/kpat/napoleon.h
new file mode 100644
index 00000000..a89f68b9
--- /dev/null
+++ b/kpat/napoleon.h
@@ -0,0 +1,56 @@
+/***********************-*-C++-*-********
+
+ napoleon.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.
+
+****************************************/
+
+
+#ifndef P_NAPOLEON
+#define P_NAPOLEON
+
+#include "dealer.h"
+
+class Napoleon : public Dealer {
+ Q_OBJECT
+public:
+ Napoleon (KMainWindow* parent=0, const char* name=0);
+
+ virtual void restart();
+ virtual void getHints();
+ virtual Card *demoNewCards();
+ virtual bool startAutoDrop() { return false; }
+ virtual bool isGameLost() const;
+
+public slots:
+ void deal1(Card *c);
+
+private:
+ void deal();
+
+ bool CanPutTarget( const Pile *c1, const CardList& c2) const;
+ bool CanPutCentre( const Pile* c1, const CardList& c2) const;
+
+ virtual bool checkAdd ( int checkIndex, const Pile *c1, const CardList& c2) const;
+
+ Pile* pile;
+ Pile* target[4];
+ Pile* centre;
+ Pile* store[4];
+ Deck* deck;
+};
+
+#endif
diff --git a/kpat/pile.cpp b/kpat/pile.cpp
new file mode 100644
index 00000000..dd419580
--- /dev/null
+++ b/kpat/pile.cpp
@@ -0,0 +1,463 @@
+#include "pile.h"
+#include "dealer.h"
+#include <kdebug.h>
+#include <qpainter.h>
+#include "cardmaps.h"
+#include <assert.h>
+#include "speeds.h"
+
+const int Pile::RTTI = 1002;
+
+const int Pile::Default = 0x0000;
+const int Pile::disallow = 0x0001;
+const int Pile::several = 0x0002; // default: move one card
+
+// Add-flags
+const int Pile::addSpread = 0x0100;
+
+// Remove-flags
+const int Pile::autoTurnTop = 0x0200;
+const int Pile::wholeColumn = 0x0400;
+
+
+
+Pile::Pile( int _index, Dealer* _dealer)
+ : QCanvasRectangle( _dealer->canvas() ),
+ m_dealer(_dealer),
+ m_atype(Custom),
+ m_rtype(Custom),
+ myIndex(_index),
+ _target(false)
+{
+ // Make the patience aware of this pile.
+ dealer()->addPile(this);
+
+ QCanvasRectangle::setVisible(true); // default
+ _checkIndex = -1;
+ m_addFlags = 0;
+ m_removeFlags = 0;
+
+ setBrush(Qt::black);
+ setPen(QPen(Qt::black));
+
+ setZ(0);
+ initSizes();
+}
+
+
+void Pile::initSizes()
+{
+ setSpread( cardMap::CARDY() / 5 + 1 );
+ setHSpread( cardMap::CARDX() / 9 + 1 );
+ setDSpread( cardMap::CARDY() / 8 );
+
+ setSize( cardMap::CARDX(), cardMap::CARDY() );
+}
+
+void Pile::setType(PileType type)
+{
+ setAddType(type);
+ setRemoveType(type);
+}
+
+void Pile::setAddType(PileType _type)
+{
+ m_atype = _type;
+ switch (_type) {
+ case Custom:
+ case FreeCell:
+ break;
+ case KlondikeTarget:
+ setTarget(true);
+ break;
+ case KlondikeStore:
+ case GypsyStore:
+ case FreecellStore:
+ setAddFlags(Pile::addSpread | Pile::several);
+ break;
+ }
+}
+
+void Pile::setRemoveType(PileType _type)
+{
+ m_rtype = _type;
+ switch (_type) {
+ case Custom:
+ break;
+ case KlondikeTarget:
+ setRemoveFlags(Pile::disallow);
+ break;
+ case KlondikeStore:
+ case GypsyStore:
+ case FreeCell:
+ break;
+ case FreecellStore:
+ setRemoveFlags(Pile::several | Pile::autoTurnTop);
+ break;
+ }
+}
+
+Pile::~Pile()
+{
+ dealer()->removePile(this);
+
+ for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it)
+ {
+ if ((*it)->source() != this) {
+ int i = -13;
+ if ((*it)->source())
+ i = (*it)->source()->index();
+ kdDebug(11111) << "pile doesn't match " << index() << " - " << i << endl;
+ }
+ (*it)->setSource(0);
+ }
+}
+
+void Pile::resetCache()
+{
+ cache.resize(0, 0);
+ cache_selected.resize(0, 0);
+}
+
+void Pile::drawShape ( QPainter & painter )
+{
+ if (isSelected()) {
+ if (cache.isNull())
+ dealer()->drawPile(cache, this, false);
+ painter.drawPixmap(int(x()), int(y()), cache);
+ } else {
+ if (cache_selected.isNull())
+ dealer()->drawPile(cache_selected, this, true);
+ painter.drawPixmap(int(x()), int(y()), cache_selected);
+ }
+}
+
+bool Pile::legalAdd( const CardList& _cards ) const
+{
+ if ( m_addFlags & disallow )
+ return false;
+
+ if ( !( m_addFlags & several ) && _cards.count() > 1 )
+ return false;
+
+ // getHint removes cards without turning, so it could be it
+ // checks later if cards can be added to a face down card
+ if (top() && !top()->realFace())
+ return false;
+
+ switch (addType()) {
+ case Custom:
+ return dealer()->checkAdd( checkIndex(), this, _cards );
+ break;
+ case KlondikeTarget:
+ return add_klondikeTarget(_cards);
+ break;
+ case FreecellStore:
+ case KlondikeStore:
+ return add_klondikeStore(_cards);
+ break;
+ case GypsyStore:
+ return add_gypsyStore(_cards);
+ break;
+ case FreeCell:
+ return add_freeCell(_cards);
+ }
+ return false;
+}
+
+bool Pile::legalRemove(const Card *c) const
+{
+ if ( m_removeFlags & disallow ) {
+ return false;
+ }
+ if ( !( m_removeFlags & several ) && top() != c)
+ return false;
+
+ switch (removeType()) {
+ case Custom:
+ return dealer()->checkRemove( checkIndex(), this, c);
+ break;
+ case KlondikeTarget:
+ case GypsyStore:
+ case KlondikeStore:
+ break;
+ case FreecellStore:
+ return remove_freecellStore(c);
+ break;
+ case FreeCell:
+ return (top() == c);
+ break;
+ }
+ return true;
+}
+
+void Pile::setVisible(bool vis)
+{
+ QCanvasRectangle::setVisible(vis);
+ dealer()->enlargeCanvas(this);
+
+ for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it)
+ {
+ (*it)->setVisible(vis);
+ dealer()->enlargeCanvas(*it);
+ }
+}
+
+void Pile::moveBy(double dx, double dy)
+{
+ QCanvasRectangle::moveBy(dx, dy);
+ dealer()->enlargeCanvas(this);
+
+ for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it)
+ {
+ (*it)->moveBy(dx, dy);
+ dealer()->enlargeCanvas(*it);
+ }
+}
+
+int Pile::indexOf(const Card *c) const
+{
+ assert(c->source() == this);
+ return m_cards.findIndex(const_cast<Card*>(c)); // the list is of non-const cards
+}
+
+Card *Pile::at(int index) const
+{
+ if (index < 0 || index >= int(m_cards.count()))
+ return 0;
+ return *m_cards.at(index);
+}
+
+// Return the top card of this pile.
+//
+
+Card *Pile::top() const
+{
+ if (m_cards.isEmpty())
+ return 0;
+
+ return m_cards.last();
+}
+
+void Pile::clear()
+{
+ for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it)
+ {
+ (*it)->setSource(0);
+ }
+ m_cards.clear();
+}
+
+void Pile::add( Card *_card, int index)
+{
+ if (_card->source() == this)
+ return;
+
+ Pile *source = _card->source();
+ if (source) {
+ _card->setTakenDown(source->target() && !target());
+ source->remove(_card);
+ }
+
+ _card->setSource(this);
+
+ if (index == -1)
+ m_cards.append(_card);
+ else {
+ while (m_cards.count() <= uint(index))
+ m_cards.append(0);
+ assert(m_cards[index] == 0);
+ m_cards[index] = _card;
+ }
+}
+
+
+// Return the number of pixels in x and y that the card should be
+// offset from the start position of the pile.
+//
+// Note: Default is to only have vertical spread (Y direction).
+
+QSize Pile::cardOffset( bool _spread, bool _facedown, const Card *before) const
+{
+ if (_spread) {
+ if (_facedown)
+ return QSize(0, dspread());
+ else {
+ if (before && !before->isFaceUp())
+ return QSize(0, dspread());
+ else
+ return QSize(0, spread());
+ }
+ }
+
+ return QSize(0, 0);
+}
+
+/* override cardtype (for initial deal ) */
+void Pile::add( Card* _card, bool _facedown, bool _spread )
+{
+ if (!_card)
+ return;
+
+ // The top card
+ Card *t = top();
+
+ // If this pile is visible, then also show the card.
+ if (isVisible())
+ _card->show();
+ else
+ _card->hide();
+
+ _card->turn( !_facedown );
+
+ QSize offset = cardOffset(_spread, _facedown, t);
+
+ int x2, y2, z2;
+
+ if (t) {
+ x2 = int(t->realX() + offset.width());
+ y2 = int(t->realY() + offset.height());
+ z2 = int(t->realZ() + 1);
+ } else {
+ x2 = int(x());
+ y2 = int(y());
+ z2 = int(z() + 1);
+ }
+
+ add(_card);
+
+ if (_facedown || !isVisible()) {
+ _card->move( x2, y2 );
+ _card->setZ( z2 );
+ } else {
+ _card->moveTo(x2, y2, z2, STEPS_INITIALDEAL);
+ }
+
+ dealer()->enlargeCanvas(_card);
+}
+
+void Pile::remove(Card *c)
+{
+ assert(m_cards.contains(c));
+ m_cards.remove(c);
+}
+
+void Pile::hideCards( const CardList & cards )
+{
+ for (CardList::ConstIterator it = cards.begin(); it != cards.end(); ++it)
+ m_cards.remove(*it);
+}
+
+void Pile::unhideCards( const CardList & cards )
+{
+ for (CardList::ConstIterator it = cards.begin(); it != cards.end(); ++it)
+ m_cards.append(*it);
+}
+
+CardList Pile::cardPressed(Card *c)
+{
+ CardList result;
+
+ if (!legalRemove(c))
+ return result;
+
+ int below = -1;
+
+ if (!c->isFaceUp())
+ return result;
+
+ for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it)
+ {
+ if (c == *it) {
+ below = 0;
+ }
+ if (below >= 0) {
+ (*it)->setAnimated(false);
+ (*it)->setZ(128 + below);
+ below++;
+ result.append(*it);
+ }
+ }
+ return result;
+}
+
+void Pile::moveCards(CardList &cl, Pile *to)
+{
+ if (!cl.count())
+ return;
+
+ for (CardList::Iterator it = cl.begin(); it != cl.end(); ++it)
+ to->add(*it);
+
+ if (m_removeFlags & autoTurnTop && top()) {
+ Card *t = top();
+ if (!t->isFaceUp()) {
+ t->flipTo(int(t->x()), int(t->y()), 8);
+ canvas()->update();
+ }
+ }
+
+ to->moveCardsBack(cl, false);
+}
+
+void Pile::moveCardsBack(CardList &cl, bool anim)
+{
+ if (!cl.count())
+ return;
+
+ Card *c = cl.first();
+
+ Card *before = 0;
+ QSize off;
+
+ int steps = STEPS_MOVEBACK;
+ if (!anim)
+ steps = 0;
+
+ for (CardList::Iterator it = m_cards.begin(); it != m_cards.end(); ++it)
+ {
+ if (c == *it) {
+ if (before) {
+ off = cardOffset(m_addFlags & Pile::addSpread, false, before);
+ c->moveTo( before->realX() + off.width(),
+ before->realY() + off.height(),
+ before->realZ() + 1, steps);
+ dealer()->enlargeCanvas(c);
+ }
+ else {
+ c->moveTo( int(x()), int(y()), int(z()) + 1, steps);
+ }
+ break;
+ } else
+ before = *it;
+ }
+
+ before = c;
+ CardList::Iterator it = cl.begin(); // == c
+ ++it;
+
+ off = cardOffset(m_addFlags & Pile::addSpread, false, 0);
+
+ for (; it != cl.end(); ++it)
+ {
+ (*it)->moveTo( before->realX() + off.width(),
+ before->realY() + off.height(),
+ before->realZ() + 1, steps);
+ dealer()->enlargeCanvas(*it);
+ before = *it;
+ }
+}
+
+bool Pile::cardClicked(Card *c)
+{
+ emit clicked(c);
+ return false;
+}
+
+bool Pile::cardDblClicked(Card *c)
+{
+ emit dblClicked(c);
+ return false;
+}
+
+#include "pile.moc"
diff --git a/kpat/pile.h b/kpat/pile.h
new file mode 100644
index 00000000..b2b553b0
--- /dev/null
+++ b/kpat/pile.h
@@ -0,0 +1,154 @@
+#ifndef _PILE_H
+#define _PILE_H
+
+
+#include "card.h"
+#include <kpixmap.h>
+
+
+class Dealer;
+
+
+/**
+ *
+ * Pile -- A pile on the board that can hold cards.
+ *
+ */
+
+class Pile : public QObject, public QCanvasRectangle
+{
+ Q_OBJECT
+
+public:
+
+ enum PileType { Custom,
+ KlondikeTarget,
+ KlondikeStore,
+ GypsyStore,
+ FreeCell,
+ FreecellStore};
+
+ // Add- and remove-flags
+ static const int Default;
+ static const int disallow;
+ static const int several; // default: move one card
+
+ // Add-flags
+ static const int addSpread;
+
+ // Remove-flags
+ static const int autoTurnTop;
+ static const int wholeColumn;
+
+ Pile( int _index, Dealer* parent = 0);
+ virtual ~Pile();
+
+ Dealer *dealer() const { return m_dealer; }
+ CardList cards() const { return m_cards; }
+
+ bool legalAdd(const CardList &c ) const;
+ bool legalRemove(const Card *c) const;
+
+ virtual void moveCards(CardList &c, Pile *to = 0);
+ void moveCardsBack(CardList &c, bool anim = true);
+
+ void setAddFlags( int flag ) { m_addFlags = flag; }
+ void setRemoveFlags( int flag ) { m_removeFlags = flag; }
+
+ void setCheckIndex( int index ) { _checkIndex = index; }
+ virtual int checkIndex() const { return _checkIndex; }
+
+ void setTarget(bool t) { _target = t; }
+ bool target() const { return _target; }
+
+ CardList cardPressed(Card *c);
+
+ Card *top() const;
+
+ void add( Card *c, bool facedown, bool spread); // for initial deal
+ void add( Card *c, int index = -1);
+ void remove(Card *c);
+ void clear();
+
+ int index() const { return myIndex; }
+ bool isEmpty() const { return m_cards.count() == 0; }
+
+ virtual void drawShape ( QPainter & p );
+ static const int RTTI;
+
+ virtual int rtti() const { return RTTI; }
+
+ virtual void setVisible(bool vis);
+ virtual void moveBy(double dx, double dy);
+
+ int cardsLeft() const { return m_cards.count(); }
+
+ int indexOf(const Card *c) const;
+ Card *at(int index) const;
+
+ void hideCards( const CardList & cards );
+ void unhideCards( const CardList & cards );
+
+ virtual QSize cardOffset( bool _spread, bool _facedown, const Card *before) const;
+
+ void resetCache();
+ virtual void initSizes();
+
+ void setType( PileType t);
+ void setAddType( PileType t);
+ void setRemoveType( PileType t);
+ PileType addType() const { return m_atype; }
+ PileType removeType() const { return m_rtype; }
+
+ // pile_algorithms
+ bool add_klondikeTarget( const CardList& c2 ) const;
+ bool add_klondikeStore( const CardList& c2 ) const;
+ bool add_gypsyStore( const CardList& c2 ) const;
+ bool add_freeCell( const CardList& c2) const;
+
+ bool remove_freecellStore( const Card *c) const;
+
+ // The spread properties.
+ int spread() const { return _spread; }
+ void setSpread(int s) { _spread = s; }
+ int dspread() const { return _dspread; }
+ void setDSpread(int s) { _dspread = s; }
+ int hspread() const { return _hspread; }
+ void setHSpread(int s) { _hspread = s; }
+
+public slots:
+ virtual bool cardClicked(Card *c);
+ virtual bool cardDblClicked(Card *c);
+
+signals:
+ void clicked(Card *c);
+ void dblClicked(Card *c);
+
+protected:
+ int m_removeFlags;
+ int m_addFlags;
+ CardList m_cards;
+
+private:
+ // Reference to the patience this pile is a part of.
+ Dealer *m_dealer;
+
+ // Properties of the pile.
+ PileType m_atype; // Addtype
+ PileType m_rtype; // Removetype
+ int _spread;
+ int _hspread;
+ int _dspread;
+
+ int _checkIndex;
+ int myIndex;
+ bool _target;
+
+ // Graphics
+ KPixmap cache;
+ KPixmap cache_selected;
+};
+
+typedef QValueList<Pile*> PileList;
+
+#endif
diff --git a/kpat/pile_algorithms.cpp b/kpat/pile_algorithms.cpp
new file mode 100644
index 00000000..7ca3469c
--- /dev/null
+++ b/kpat/pile_algorithms.cpp
@@ -0,0 +1,69 @@
+#include "pile.h"
+#include <kdebug.h>
+
+bool Pile::add_klondikeTarget( const CardList& c2 ) const
+{
+ Card *newone = c2.first();
+ if (isEmpty())
+ return (newone->rank() == Card::Ace);
+
+ return (newone->rank() == top()->rank() + 1)
+ && (top()->suit() == newone->suit());
+}
+
+bool Pile::add_klondikeStore( const CardList& c2 ) const
+{
+ Card *newone = c2.first();
+ if (isEmpty()) {
+ return (newone->rank() == Card::King);
+ }
+
+ return (newone->rank() == top()->rank() - 1)
+ && (top()->isRed() != newone->isRed());
+}
+
+bool Pile::add_gypsyStore( const CardList& c2) const
+{
+ Card *newone = c2.first();
+ if (isEmpty())
+ return true;
+
+ return (newone->rank() == top()->rank() - 1)
+ && (top()->isRed() != newone->isRed());
+}
+
+bool Pile::add_freeCell( const CardList & cards) const
+{
+ return (cards.count() == 1 && isEmpty());
+}
+
+bool Pile::remove_freecellStore( const Card *c) const
+{
+ // ok if just one card
+ if (c == top())
+ return true;
+
+ // Now we're trying to move two or more cards.
+
+ // First, let's check if the column is in valid
+ // (that is, in sequence, alternated colors).
+ int index = indexOf(c) + 1;
+ const Card *before = c;
+ while (true)
+ {
+ c = at(index++);
+
+ if (!((c->rank() == (before->rank()-1))
+ && (c->isRed() != before->isRed())))
+ {
+ kdDebug(11111) << c->name() << " - " << before->name() << endl;
+ return false;
+ }
+ if (c == top())
+ return true;
+ before = c;
+ }
+
+ return true;
+}
+
diff --git a/kpat/pwidget.cpp b/kpat/pwidget.cpp
new file mode 100644
index 00000000..dae69f60
--- /dev/null
+++ b/kpat/pwidget.cpp
@@ -0,0 +1,560 @@
+/*
+ patience -- main program
+ 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.
+
+
+ Heavily modified by Mario Weilguni <mweilguni@sime.com>
+*/
+
+#include <stdio.h>
+
+#include <qregexp.h>
+#include <qtimer.h>
+#include <qimage.h>
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <kaction.h>
+#include <kdebug.h>
+#include <kcarddialog.h>
+#include <kinputdialog.h>
+#include <kstandarddirs.h>
+#include <kfiledialog.h>
+#include <ktempfile.h>
+#include <kio/netaccess.h>
+#include <kmessagebox.h>
+#include <kstatusbar.h>
+#include <kaccelmanager.h>
+#include <kmenubar.h>
+
+#include "pwidget.h"
+#include "version.h"
+#include "dealer.h"
+#include "cardmaps.h"
+#include "speeds.h"
+#include "gamestatsimpl.h"
+
+
+static pWidget *current_pwidget = 0;
+
+
+void saveGame(int) {
+ current_pwidget->saveGame();
+}
+
+pWidget::pWidget()
+ : KMainWindow(0, "pwidget"), dill(0)
+{
+ current_pwidget = this;
+ // KCrash::setEmergencySaveFunction(::saveGame);
+ KStdAction::quit(kapp, SLOT(quit()), actionCollection(), "game_exit");
+
+ undo = KStdAction::undo(this, SLOT(undoMove()),
+ actionCollection(), "undo_move");
+ undo->setEnabled(false);
+ (void)KStdAction::openNew(this, SLOT(newGame()),
+ actionCollection(), "new_game");
+ (void)KStdAction::open(this, SLOT(openGame()),
+ actionCollection(), "open");
+ recent = KStdAction::openRecent(this, SLOT(openGame(const KURL&)),
+ actionCollection(), "open_recent");
+ recent->loadEntries(KGlobal::config());
+ (void)KStdAction::saveAs(this, SLOT(saveGame()),
+ actionCollection(), "save");
+ (void)new KAction(i18n("&Choose Game..."), 0, this, SLOT(chooseGame()),
+ actionCollection(), "choose_game");
+ (void)new KAction(i18n("Restart &Game"), QString::fromLatin1("reload"), 0,
+ this, SLOT(restart()),
+ actionCollection(), "restart_game");
+ (void)KStdAction::help(this, SLOT(helpGame()), actionCollection(), "help_game");
+
+ games = new KSelectAction(i18n("&Game Type"), 0, this,
+ SLOT(newGameType()),
+ actionCollection(), "game_type");
+ QStringList list;
+ QValueList<DealerInfo*>::ConstIterator it;
+ uint max_type = 0;
+
+ for (it = DealerInfoList::self()->games().begin();
+ it != DealerInfoList::self()->games().end(); ++it)
+ {
+ // while we develop, it may happen that some lower
+ // indices do not exist
+ uint index = (*it)->gameindex;
+ for (uint i = 0; i <= index; i++)
+ if (list.count() <= i)
+ list.append("unknown");
+ list[index] = i18n((*it)->name);
+ if (max_type < index)
+ max_type = index;
+ }
+ games->setItems(list);
+
+ KGlobal::dirs()->addResourceType("wallpaper", KStandardDirs::kde_default("data") + "kpat/backgrounds/");
+ KGlobal::dirs()->addResourceType("wallpaper", KStandardDirs::kde_default("data") + "ksnake/backgrounds/");
+ wallpapers = new KSelectAction(i18n("&Change Background"), 0, this,
+ SLOT(changeWallpaper()),
+ actionCollection(), "wallpaper");
+ list.clear();
+ wallpaperlist.clear();
+ QStringList wallpaperlist2 = KGlobal::dirs()->findAllResources("wallpaper", QString::null,
+ false, true, list);
+ QStringList list2;
+ for (QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
+ QString file = *it;
+ int rindex = file.findRev('.');
+ if (rindex != -1) {
+ QString ext = file.mid(rindex + 1).lower();
+ if (ext == "jpeg" || ext == "png" || ext == "jpg") {
+ list2.append(file.left(rindex));
+ wallpaperlist.append( file );
+ }
+ }
+ }
+
+ wallpapers->setItems(list2);
+ wallpapers->setCurrentItem(list2.findIndex("No-Ones-Laughing-3"));
+
+ changeWallpaper();
+
+ (void)new cardMap(midcolor);
+
+ backs = new KAction(i18n("&Switch Cards..."), 0, this,
+ SLOT(changeBackside()),
+ actionCollection(), "backside");
+ stats = new KAction(i18n("&Statistics"), 0, this, SLOT(showStats()),
+ actionCollection(),"game_stats");
+
+ animation = new KToggleAction(i18n( "&Animation on Startup" ),
+ 0, this, SLOT(animationChanged()),
+ actionCollection(), "animation");
+ dropaction = new KToggleAction(i18n("&Enable Autodrop"),
+ 0, this, SLOT(enableAutoDrop()),
+ actionCollection(), "enable_autodrop");
+ dropaction->setCheckedState(i18n("Disable Autodrop"));
+
+ KConfig *config = kapp->config();
+ KConfigGroupSaver cs(config, settings_group );
+
+ QString bgpath = config->readPathEntry("Background");
+ kdDebug(11111) << "bgpath '" << bgpath << "'" << endl;
+ if (bgpath.isEmpty())
+ bgpath = locate("wallpaper", "No-Ones-Laughing-3.jpg");
+ background = QPixmap(bgpath);
+
+ bool animate = config->readBoolEntry( "Animation", true);
+ animation->setChecked( animate );
+
+ bool autodrop = config->readBoolEntry("Autodrop", true);
+ dropaction->setChecked(autodrop);
+
+ uint game = config->readNumEntry("DefaultGame", 0);
+ if (game > max_type)
+ game = max_type;
+ games->setCurrentItem(game);
+
+ statusBar()->insertItem( "", 1, 0, true );
+
+ createGUI(QString::null, false);
+ KAcceleratorManager::manage(menuBar());
+
+ newGameType();
+ adjustSize();
+ setAutoSaveSettings();
+}
+
+pWidget::~pWidget()
+{
+ delete dill;
+}
+
+void pWidget::undoMove() {
+ if( dill )
+ dill->undo();
+}
+
+void pWidget::helpGame()
+{
+ if (!dill)
+ return;
+ kapp->invokeHelp(dill->anchorName());
+}
+
+void pWidget::undoPossible(bool poss)
+{
+ undo->setEnabled(poss);
+}
+
+void pWidget::changeBackside() {
+ KConfig *config = kapp->config();
+ KConfigGroupSaver kcs(config, settings_group);
+
+ QString deck = config->readEntry("Back", KCardDialog::getDefaultDeck());
+ QString cards = config->readEntry("Cards", KCardDialog::getDefaultCardDir());
+ if (KCardDialog::getCardDeck(deck, cards, this, KCardDialog::Both) == QDialog::Accepted)
+ {
+ QString imgname = KCardDialog::getCardPath(cards, 11);
+
+ QImage image;
+ image.load(imgname);
+ if( image.isNull()) {
+ kdDebug(11111) << "cannot load card pixmap \"" << imgname << "\" in " << cards << "\n";
+ return;
+ }
+
+ bool change = false;
+ if (image.width() != cardMap::CARDX() || image.height() != cardMap::CARDY())
+ {
+ change = true;
+ if (KMessageBox::warningContinueCancel(this, i18n("The cards you have chosen have a different "
+ "size than the ones you are currently using. "
+ "This requires the current game to be restarted.")) == KMessageBox::Cancel)
+ return;
+ }
+ setBackSide(deck, cards);
+ if (change) {
+
+ newGameType();
+ }
+ }
+
+}
+
+void pWidget::changeWallpaper()
+{
+ QString bgpath=locate("wallpaper", wallpaperlist[wallpapers->currentItem()]);
+ if (bgpath.isEmpty())
+ return;
+ background = QPixmap(bgpath);
+ if (background.isNull()) {
+ KMessageBox::sorry(this, i18n("<qt>Couldn't load wallpaper<br/>%1</qt>").arg(bgpath));
+ return;
+ }
+
+ QImage bg = background.convertToImage().convertDepth(8, 0);
+ if (bg.isNull() || !bg.numColors())
+ return;
+ long r = 0;
+ long g = 0;
+ long b = 0;
+ for (int i = 0; i < bg.numColors(); ++i)
+ {
+ QRgb rgb = bg.color(i);
+ r += qRed(rgb);
+ g += qGreen(rgb);
+ b += qBlue(rgb);
+ }
+ r /= bg.numColors();
+ b /= bg.numColors();
+ g /= bg.numColors();
+ midcolor = QColor(r, b, g);
+
+ if (dill) {
+ KConfig *config = kapp->config();
+ KConfigGroupSaver kcs(config, settings_group);
+
+ QString deck = config->readEntry("Back", KCardDialog::getDefaultDeck());
+ QString dummy = config->readEntry("Cards", KCardDialog::getDefaultCardDir());
+ setBackSide(deck, dummy);
+
+ config->writePathEntry("Background", bgpath);
+ dill->setBackgroundPixmap(background, midcolor);
+ dill->canvas()->setAllChanged();
+ dill->canvas()->update();
+ }
+}
+
+void pWidget::animationChanged() {
+ bool anim = animation->isChecked();
+ KConfig *config = kapp->config();
+ KConfigGroupSaver cs(config, settings_group );
+ config->writeEntry( "Animation", anim);
+}
+
+void pWidget::enableAutoDrop()
+{
+ bool drop = dropaction->isChecked();
+ KConfig *config = kapp->config();
+ KConfigGroupSaver cs(config, settings_group );
+ config->writeEntry( "Autodrop", drop);
+ dill->setAutoDropEnabled(drop);
+}
+
+void pWidget::newGame()
+{
+ // Check if the user is already running a game, and if she is,
+ // then ask if she wants to abort it.
+ if (!dill->isGameWon() && !dill->isGameLost()
+ && KMessageBox::warningContinueCancel(0,
+ i18n("You are already running an unfinished game. "
+ "If you abort the old game to start a new one, "
+ "the old game will be registered as a loss in "
+ "the statistics file.\n"
+ "What do you want to do?"),
+ i18n("Abort Current Game?"),
+ i18n("Abort Old Game"),
+ "careaboutstats" ) == KMessageBox::Cancel)
+ return;
+
+ dill->setGameNumber(kapp->random());
+ setGameCaption();
+ restart();
+}
+
+
+void pWidget::restart()
+{
+ statusBar()->clear();
+ dill->startNew();
+}
+
+void pWidget::setGameCaption()
+{
+ QString name = games->currentText();
+ QString newname;
+ QString gamenum;
+ gamenum.setNum( dill->gameNumber() );
+ for (uint i = 0; i < name.length(); i++)
+ if (name.at(i) != QChar('&'))
+ newname += name.at(i);
+
+ setCaption( newname + " - " + gamenum );
+}
+
+void pWidget::newGameType()
+{
+ delete dill;
+ dill = 0;
+ slotUpdateMoves();
+
+ uint id = games->currentItem();
+ for (QValueList<DealerInfo*>::ConstIterator it = DealerInfoList::self()->games().begin(); it != DealerInfoList::self()->games().end(); ++it) {
+ if ((*it)->gameindex == id) {
+ dill = (*it)->createGame(this);
+ QString name = (*it)->name;
+ name = name.replace(QRegExp("[&']"), "");
+ name = name.replace(QRegExp("[ ]"), "_").lower();
+ dill->setAnchorName("game_" + name);
+ connect(dill, SIGNAL(saveGame()), SLOT(saveGame()));
+ connect(dill, SIGNAL(gameInfo(const QString&)),
+ SLOT(slotGameInfo(const QString &)));
+ connect(dill, SIGNAL(updateMoves()),
+ SLOT(slotUpdateMoves()));
+ dill->setGameId(id);
+ dill->setupActions();
+ dill->setBackgroundPixmap(background, midcolor);
+ dill->startNew();
+ break;
+ }
+ }
+
+ if (!dill) {
+ kdError() << "unimplemented game type " << id << endl;
+ dill = DealerInfoList::self()->games().first()->createGame(this);
+ }
+
+ connect(dill, SIGNAL(undoPossible(bool)), SLOT(undoPossible(bool)));
+ connect(dill, SIGNAL(gameWon(bool)), SLOT(gameWon(bool)));
+ connect(dill, SIGNAL(gameLost()), SLOT(gameLost()));
+
+ dill->setAutoDropEnabled(dropaction->isChecked());
+
+ // it's a bit tricky - we have to do this here as the
+ // base class constructor runs before the derived class's
+ dill->takeState();
+
+ setGameCaption();
+
+ KConfig *config = kapp->config();
+ KConfigGroupSaver kcs(config, settings_group);
+ config->writeEntry("DefaultGame", id);
+
+ QSize min(700,400);
+ min = min.expandedTo(dill->minimumCardSize());
+ dill->setMinimumSize(min);
+ dill->resize(min);
+ updateGeometry();
+ setCentralWidget(dill);
+ dill->show();
+}
+
+void pWidget::showEvent(QShowEvent *e)
+{
+ if (dill)
+ dill->setMinimumSize(QSize(0,0));
+ KMainWindow::showEvent(e);
+}
+
+void pWidget::slotGameInfo(const QString &text)
+{
+ statusBar()->message(text, 3000);
+}
+
+void pWidget::slotUpdateMoves()
+{
+ int moves = 0;
+ if ( dill ) moves = dill->getMoves();
+ statusBar()->changeItem( i18n("1 move", "%n moves", moves), 1 );
+}
+
+void pWidget::setBackSide(const QString &deck, const QString &cards)
+{
+ KConfig *config = kapp->config();
+ KConfigGroupSaver kcs(config, settings_group);
+ QPixmap pm(deck);
+ if(!pm.isNull()) {
+ cardMap::self()->setBackSide(pm, false);
+ config->writeEntry("Back", deck);
+ bool ret = cardMap::self()->setCardDir(cards);
+ if (!ret) {
+ config->writeEntry("Back", "");
+
+ }
+ config->writeEntry("Cards", cards);
+ cardMap::self()->setBackSide(pm, true);
+ } else
+ KMessageBox::sorry(this,
+ i18n("Could not load background image!"));
+
+ if (dill) {
+ dill->canvas()->setAllChanged();
+ dill->canvas()->update();
+ }
+}
+
+void pWidget::chooseGame()
+{
+ bool ok;
+ long number = KInputDialog::getText(i18n("Game Number"), i18n("Enter a game number (FreeCell deals are the same as in the FreeCell FAQ):"), QString::number(dill->gameNumber()), 0, this).toLong(&ok);
+ if (ok) {
+ dill->setGameNumber(number);
+ setGameCaption();
+ restart();
+ }
+}
+
+void pWidget::gameWon(bool withhelp)
+{
+ QString congrats;
+ if (withhelp)
+ congrats = i18n("Congratulations! We have won!");
+ else
+ congrats = i18n("Congratulations! You have won!");
+#if TEST_SOLVER == 0
+ KMessageBox::information(this, congrats, i18n("Congratulations!"));
+#endif
+ QTimer::singleShot(0, this, SLOT(newGame()));
+#if TEST_SOLVER == 1
+ dill->demo();
+#endif
+}
+
+void pWidget::gameLost()
+{
+ QString dontAskAgainName = "gameLostDontAskAgain";
+
+ // The following code is taken out of kmessagebox.cpp in kdeui.
+ // Is there a better way?
+ KConfig *config = 0;
+ QString grpNotifMsgs = QString::fromLatin1("Notification Messages");
+
+ config = KGlobal::config();
+ KConfigGroupSaver saver(config,
+ QString::fromLatin1("Notification Messages"));
+ QString dontAsk = config->readEntry(dontAskAgainName).lower();
+
+ // If we are ordered never to ask again and to continue the game,
+ // then do so.
+ if (dontAsk == "no")
+ return;
+ // If it says yes, we ask anyway. Just starting a new game would
+ // be incredibly annoying.
+ if (dontAsk == "yes")
+ dontAskAgainName = QString::null;
+
+ if (KMessageBox::questionYesNo(this, i18n("You could not win this game, "
+ "but there is always a second try.\nStart a new game?"),
+ i18n("Could Not Win!"),
+ i18n("New Game"),
+ KStdGuiItem::cont(),
+ dontAskAgainName) == KMessageBox::Yes) {
+
+ QTimer::singleShot(0, this, SLOT(newGame()));
+ }
+}
+
+void pWidget::openGame(const KURL &url)
+{
+ QString tmpFile;
+ if( KIO::NetAccess::download( url, tmpFile, this ) )
+ {
+ QFile of(tmpFile);
+ of.open(IO_ReadOnly);
+ QDomDocument doc;
+ QString error;
+ if (!doc.setContent(&of, &error))
+ {
+ KMessageBox::sorry(this, error);
+ return;
+ }
+ uint id = doc.documentElement().attribute("id").toUInt();
+
+ if (id != (Q_UINT32)games->currentItem()) {
+ games->setCurrentItem(id);
+ newGameType();
+ if (!dill) {
+ KMessageBox::error(this, i18n("The saved game is of unknown type!"));
+ games->setCurrentItem(0);
+ newGameType();
+ }
+ }
+ dill->openGame(doc);
+ setGameCaption();
+ KIO::NetAccess::removeTempFile( tmpFile );
+ recent->addURL(url);
+ recent->saveEntries(KGlobal::config());
+ }
+}
+
+void pWidget::openGame()
+{
+ KURL url = KFileDialog::getOpenURL();
+ openGame(url);
+}
+
+void pWidget::saveGame()
+{
+ KURL url = KFileDialog::getSaveURL();
+ KTempFile file;
+ QDomDocument doc("kpat");
+ dill->saveGame(doc);
+ QTextStream *stream = file.textStream();
+ *stream << doc.toString();
+ file.close();
+ KIO::NetAccess::upload(file.name(), url, this);
+ recent->addURL(url);
+ recent->saveEntries(KGlobal::config());
+}
+
+void pWidget::showStats()
+{
+ GameStatsImpl* dlg = new GameStatsImpl(this,"statistics dialog");
+ if (dill)
+ dlg->showGameType(dill->gameId());
+ dlg->exec();
+}
+
+#include "pwidget.moc"
+
diff --git a/kpat/pwidget.h b/kpat/pwidget.h
new file mode 100644
index 00000000..8781960f
--- /dev/null
+++ b/kpat/pwidget.h
@@ -0,0 +1,90 @@
+/* -*- C++ -*-
+ *
+ * patience -- main program
+ * 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.
+ *
+ *
+ * Heavily modified by Mario Weilguni <mweilguni@sime.com>
+ *
+ */
+
+#ifndef __PWIDGET__H__
+#define __PWIDGET__H__
+
+#include <kmainwindow.h>
+
+class Dealer;
+class KToggleAction;
+class KSelectAction;
+class KRecentFilesAction;
+class KAction;
+class QWidgetStack;
+class QLabel;
+
+class pWidget: public KMainWindow {
+ Q_OBJECT
+
+public:
+ pWidget();
+ ~pWidget();
+
+public slots:
+ void undoMove();
+ void changeBackside();
+ void animationChanged();
+ void newGameType();
+ void restart();
+
+ void openGame();
+ void openGame(const KURL &url);
+ void saveGame();
+
+ void newGame();
+ void chooseGame();
+ void undoPossible(bool poss);
+ void gameWon(bool withhelp);
+ void gameLost();
+ void changeWallpaper();
+ void slotGameInfo(const QString &);
+ void slotUpdateMoves();
+ void helpGame();
+ void enableAutoDrop();
+ void showStats();
+
+private:
+ void setGameCaption();
+ void setBackSide(const QString &deck, const QString &dir);
+ virtual void showEvent(QShowEvent *e);
+
+private:
+ // Members
+
+ Dealer *dill; // The current patience
+
+ KSelectAction *games;
+ KSelectAction *wallpapers;
+ KAction *backs;
+ KAction *undo;
+ KToggleAction *animation;
+ KToggleAction *dropaction;
+ KAction *stats;
+
+ QPixmap background;
+ QColor midcolor;
+ QStringList wallpaperlist;
+ KRecentFilesAction *recent;
+};
+
+#endif
diff --git a/kpat/simon.cpp b/kpat/simon.cpp
new file mode 100644
index 00000000..287ecd00
--- /dev/null
+++ b/kpat/simon.cpp
@@ -0,0 +1,156 @@
+#include "simon.h"
+#include <klocale.h>
+#include <kdebug.h>
+#include "deck.h"
+#include <assert.h>
+#include "cardmaps.h"
+
+Simon::Simon( KMainWindow* parent, const char *name )
+ : Dealer( parent, name )
+{
+ deck = Deck::new_deck(this);
+ deck->move(10, 10);
+ deck->hide();
+
+ const int dist_x = cardMap::CARDX() * 11 / 10 + 1;
+
+ for (int i=0; i<4; i++) {
+ target[i] = new Pile(i+1, this);
+ target[i]->move(10+(i+3)*dist_x, 10);
+ target[i]->setRemoveFlags(Pile::disallow);
+ target[i]->setAddFlags(Pile::several);
+ target[i]->setCheckIndex(0);
+ target[i]->setTarget(true);
+ }
+
+ for (int i=0; i<10; i++) {
+ store[i] = new Pile(5+i, this);
+ store[i]->move(15+dist_x*i, 10 + cardMap::CARDY() * 73 / 50);
+ store[i]->setAddFlags(Pile::addSpread | Pile::several);
+ store[i]->setRemoveFlags(Pile::several);
+ store[i]->setCheckIndex(1);
+ }
+
+ setActions(Dealer::Hint | Dealer::Demo);
+}
+
+void Simon::restart() {
+ deck->collectAndShuffle();
+ deal();
+}
+
+void Simon::deal() {
+ int piles = 3;
+
+ for (int round = 0; round < 8; round++)
+ {
+ for (int j = 0; j < piles; j++)
+ {
+ store[j]->add(deck->nextCard(), false, true);
+ }
+ piles++;
+ }
+ assert(deck->isEmpty());
+}
+
+bool Simon::checkPrefering( int checkIndex, const Pile *c1, const CardList& c2) const
+{
+ if (checkIndex == 1) {
+ if (c1->isEmpty())
+ return false;
+
+ return (c1->top()->suit() == c2.first()->suit());
+ } else return false; // it's just important to keep this unique
+}
+
+bool Simon::checkAdd( int checkIndex, const Pile *c1, const CardList& c2) const
+{
+ if (checkIndex == 1) {
+ if (c1->isEmpty())
+ return true;
+
+ return (c1->top()->rank() == c2.first()->rank() + 1);
+ } else {
+ if (!c1->isEmpty())
+ return false;
+ return (c2.first()->rank() == Card::King && c2.last()->rank() == Card::Ace);
+ }
+}
+
+bool Simon::checkRemove(int checkIndex, const Pile *p, const Card *c) const
+{
+ if (checkIndex != 1)
+ return false;
+
+ // ok if just one card
+ if (c == p->top())
+ return true;
+
+ // Now we're trying to move two or more cards.
+
+ // First, let's check if the column is in valid
+ // (that is, in sequence, alternated colors).
+ int index = p->indexOf(c) + 1;
+ const Card *before = c;
+ while (true)
+ {
+ c = p->at(index++);
+
+ if (!((c->rank() == (before->rank()-1))
+ && (c->suit() == before->suit())))
+ {
+ return false;
+ }
+ if (c == p->top())
+ return true;
+ before = c;
+ }
+
+ return true;
+}
+
+bool Simon::isGameLost() const
+{
+ kdDebug(11111) <<"isGameLost" << endl;
+ for (int i=0; i<10; i++) {
+ if(store[i]->isEmpty())
+ return false;
+ kdDebug(11111) <<"store["<<i<<"]" << endl;
+
+ Card *c;
+ Card *top=store[i]->top();
+ int indexi=store[i]->indexOf(top);
+ while(--indexi >=0){
+ kdDebug(11111) <<top->name() << endl;
+ c=store[i]->at(indexi);
+ if(c->suit() == top->suit() &&
+ (top->rank()+1) == c->rank())
+ top=c;
+ else
+ break;
+ }
+
+ kdDebug(11111) <<"selected: " << top->name() << endl;
+ for(int j=1; j <10; j++){
+ int k=(i+j) % 10;
+
+ if(store[k]->isEmpty())
+ return false;
+
+ kdDebug(11111) <<"vs "<<store[k]->top()->name() << endl;
+ if((top->rank() +1) == store[k]->top()->rank())
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static class LocalDealerInfo9 : public DealerInfo
+{
+public:
+ LocalDealerInfo9() : DealerInfo(I18N_NOOP("&Simple Simon"), 9) {}
+ virtual Dealer *createGame(KMainWindow *parent) { return new Simon(parent); }
+} gfi9;
+
+#include "simon.moc"
diff --git a/kpat/simon.h b/kpat/simon.h
new file mode 100644
index 00000000..91a50a1b
--- /dev/null
+++ b/kpat/simon.h
@@ -0,0 +1,29 @@
+#ifndef SIMON_H
+#define SIMON_H
+
+#include "dealer.h"
+
+class Simon : public Dealer {
+ Q_OBJECT
+
+public:
+ Simon( KMainWindow* parent=0, const char* name=0);
+
+public slots:
+ void deal();
+ virtual void restart();
+ virtual bool isGameLost() const;
+
+
+protected:
+ virtual bool checkAdd ( int checkIndex, const Pile *c1, const CardList& c2) const;
+ virtual bool checkPrefering( int checkIndex, const Pile *c1, const CardList& c2) const;
+ virtual bool checkRemove( int checkIndex, const Pile *c1, const Card *c) const;
+
+private:
+ Pile* store[10];
+ Pile* target[4];
+ Deck *deck;
+};
+
+#endif
diff --git a/kpat/speeds.h b/kpat/speeds.h
new file mode 100644
index 00000000..9f2c646d
--- /dev/null
+++ b/kpat/speeds.h
@@ -0,0 +1,26 @@
+#ifndef __SPEEDS_H_
+#define __SPEEDS_H_
+
+#define TEST_SOLVER 0
+
+#ifdef TEST_SOLVER
+#define STEPS_AUTODROP 8
+#define STEPS_WON 20
+#define STEPS_DEMO 7
+#define STEPS_MOVEBACK 7
+#define STEPS_INITIALDEAL 10
+
+#define TIME_BETWEEN_MOVES 200
+#else
+
+#define STEPS_AUTODROP 1
+#define STEPS_WON 20
+#define STEPS_DEMO 1
+#define STEPS_MOVEBACK 1
+#define STEPS_INITIALDEAL 1
+
+#define TIME_BETWEEN_MOVES 2
+
+#endif
+
+#endif
diff --git a/kpat/spider.cpp b/kpat/spider.cpp
new file mode 100644
index 00000000..262c49b9
--- /dev/null
+++ b/kpat/spider.cpp
@@ -0,0 +1,484 @@
+/*---------------------------------------------------------------------------
+
+ spider.cpp implements a patience card game
+
+ Copyright (C) 2003 Josh Metzler
+
+ * 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 "spider.h"
+#include "cardmaps.h"
+#include <klocale.h>
+#include "deck.h"
+#include <kdebug.h>
+
+void SpiderPile::moveCards(CardList &c, Pile *to)
+{
+ Pile::moveCards(c, to);
+
+ // if this is a leg pile, don't do anything special
+ if ( to->checkIndex() == 0 )
+ return;
+
+ // if the top card of the list I just moved is an Ace,
+ // the run I just moved is the same suit as the pile,
+ // and the destination pile now has more than 12 cards,
+ // then it could have a full deck that needs removed.
+ if (c.last()->rank() == Card::Ace &&
+ c.first()->suit() == to->top()->suit() &&
+ to->cardsLeft() > 12) {
+ Spider *b = dynamic_cast<Spider*>(dealer());
+ if (b) {
+ b->checkPileDeck(to);
+ }
+ }
+}
+
+//-------------------------------------------------------------------------//
+
+Spider::Spider(int suits, KMainWindow* 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;
+
+ deck = Deck::new_deck(this, 2, suits);
+
+ // I deal the cards into 'redeal' piles, so hide the deck
+ deck->setVisible(false);
+
+ // Dealing the cards out into 5 piles so the user can see how many
+ // sets of 10 cards are left to be dealt out
+ for( int column = 0; column < 5; column++ ) {
+ redeals[column] = new Pile(column + 1, this);
+ redeals[column]->move(8 + dist_x / 3 * (23 + column), 8 + dist_y * 4.5);
+ redeals[column]->setZ(5-column);
+ redeals[column]->setCheckIndex(0);
+ redeals[column]->setAddFlags(Pile::disallow);
+ redeals[column]->setRemoveFlags(Pile::disallow);
+ connect(redeals[column], SIGNAL(clicked(Card*)), SLOT(deckClicked(Card*)));
+ }
+
+ // The 10 playing piles
+ for( int column = 0; column < 10; column++ ) {
+ stack[column] = new SpiderPile(column + 6, this);
+ stack[column]->move(8 + dist_x * column, 8);
+ stack[column]->setZ(20);
+ stack[column]->setCheckIndex(1);
+ stack[column]->setAddFlags(Pile::addSpread | Pile::several);
+ stack[column]->setRemoveFlags(Pile::several |
+ Pile::autoTurnTop | Pile::wholeColumn);
+ }
+
+ // The 8 'legs' so named by me because spiders have 8 legs - why
+ // else the name Spider?
+ for( int column = 0; column < 8; column++ ) {
+ legs[column] = new Pile(column + 16, this);
+ legs[column]->move(8 + dist_x / 3 * column, 8 + dist_y * 4.5);
+ legs[column]->setZ(column+1);
+ legs[column]->setCheckIndex(0);
+ legs[column]->setAddFlags(Pile::disallow);
+ legs[column]->setRemoveFlags(Pile::disallow);
+ legs[column]->setTarget(true);
+ }
+
+ // Moving an A-K run to a leg is not really an autoDrop - the
+ // user should have no choice. Also, it must be moved A first, ...
+ // up to K so the King will be on top.
+ setAutoDropEnabled(false);
+ setActions(Dealer::Hint | Dealer::Demo );
+}
+
+//-------------------------------------------------------------------------//
+
+bool Spider::checkAdd(int /*checkIndex*/, const Pile *c1, const CardList& c2) const
+{
+ // assuming the cardlist is a valid unit, since I allowed
+ // it to be removed - can drop any card on empty pile or
+ // on any suit card of one higher rank
+ if (c1->isEmpty() || c1->top()->rank() == c2.first()->rank()+1)
+ return true;
+
+ return false;
+}
+
+bool Spider::checkRemove(int /*checkIndex*/, const Pile *p, const Card *c) const
+{
+ // if the pile from c up is decreasing by 1 and all the same suit, ok
+ // note that this is true if c is the top card
+ const Card *before;
+ int index = p->indexOf(c);
+ while (c != p->top()) {
+ before = c;
+ c = p->at(++index);
+ if (before->suit() != c->suit() || before->rank() != c->rank()+1)
+ return false;
+ }
+ return true;
+}
+
+void Spider::getHints()
+{
+ kdDebug(11111) << "get hints" << endl;
+ // first, get runs from each stack
+ CardList cl[10];
+
+ Pile* empty = NULL;
+ for (int column = 0; column < 10; column++) {
+ if (stack[column]->isEmpty())
+ empty = stack[column];
+ else
+ cl[column] = getRun(stack[column]->top());
+ }
+
+ // if I can build a run from Ace->King in one suit then
+ // hint those moves
+ HintList hl;
+ for (int s = Card::Clubs; s <= Card::Spades; s++) {
+ bool bGrowing = true;
+ int vTopNew = 0;
+ int colNew = -1;
+ while (bGrowing && vTopNew < 13) {
+ bGrowing = false;
+ int col = colNew;
+ int vTop = vTopNew;
+ for (int column = 0; column < 10; column++) {
+ if (cl[column].isEmpty() || col == column)
+ continue;
+ if (cl[column].last()->suit() == s &&
+ cl[column].last()->rank() <= vTop+1 &&
+ cl[column].first()->rank() > vTop)
+ {
+ bGrowing = true;
+ if (cl[column].first()->rank() > vTopNew) {
+ colNew = column;
+ vTopNew = cl[column].first()->rank();
+ }
+ }
+ }
+ if (bGrowing && vTop)
+ hl.append(new MoveHint(cl[col][vTop-
+ cl[colNew].last()->rank()+1], stack[colNew]));
+ }
+ if (vTopNew == 13)
+ hints += hl;
+ else
+ for (HintList::Iterator it = hl.begin(); it != hl.end(); ++it)
+ delete *it;
+ hl.clear();
+ }
+
+ // now check to see if a run from one column can go on the end
+ // of a run from another stack
+ for (int column = 0; column < 10; column++) {
+ if (cl[column].isEmpty())
+ continue;
+
+ // if there is an empty column and this stack is on
+ // another card, hint
+ if (empty && cl[column].count() < (uint)stack[column]->cardsLeft()) {
+ newHint(new MoveHint(cl[column].first(), empty));
+ continue;
+ }
+
+ // now see if I can move this stack to any other column
+ for (int c2 = 0; c2 < 10; c2++) {
+ if (c2 == column || cl[c2].isEmpty())
+ continue;
+
+ if (cl[c2].last()->rank() == cl[column].first()->rank()+1)
+ {
+ // I can hint this move - should I?
+ int index = stack[column]->indexOf(cl[column].first());
+
+ // if target pile is the same suit as this card,
+ // or if there are no cards under this one,
+ // or if it couldn't move to where it is now,
+ // or if the card under this one is face down, hint
+ if (cl[c2].last()->suit() == cl[column].first()->suit() ||
+ index == 0 || stack[column]->at(index-1)->rank() !=
+ cl[column].first()->rank()+1 ||
+ !(stack[column]->at(index-1)->realFace()))
+ newHint(new MoveHint(cl[column].first(), stack[c2]));
+ }
+ }
+ }
+}
+
+MoveHint *Spider::chooseHint()
+{
+ kdDebug(11111) << "choose 1 of " << hints.count() << " hints" << endl;
+ if (hints.isEmpty())
+ return 0;
+
+ // first, choose a card that is moving to the same suit
+ for (HintList::ConstIterator it = hints.begin(); it != hints.end(); ++it)
+ {
+ if (!(*it)->pile()->isEmpty() &&
+ (*it)->pile()->top()->suit() == (*it)->card()->suit())
+ return *it;
+ }
+
+ // second, choose a card that is moving from the base
+ for (HintList::ConstIterator it = hints.begin(); it != hints.end(); ++it)
+ {
+ if ((*it)->card()->source() &&
+ (*it)->card()->source()->at(0) == (*it)->card())
+ return *it;
+ }
+
+ // otherwise, go with a random hint
+ return hints[randseq.getLong(hints.count())];
+}
+
+//-------------------------------------------------------------------------//
+
+QString Spider::getGameState() const
+{
+ return QString::number(m_leg*10 + m_redeal);
+}
+
+void Spider::setGameState(const QString &stream)
+{
+ int i = stream.toInt();
+
+ if (m_leg > i/10) {
+ for (m_leg--; m_leg > i/10; m_leg--)
+ legs[m_leg]->setVisible(false);
+ legs[m_leg]->setVisible(false);
+ } else
+ for (; m_leg < i/10; m_leg++)
+ legs[m_leg]->setVisible(true);
+
+ if (m_redeal > i%10) {
+ for (m_redeal--; m_redeal > i%10; m_redeal--)
+ redeals[m_redeal]->setVisible(true);
+ redeals[m_redeal]->setVisible(true);
+ } else
+ for (; m_redeal < i%10; m_redeal++)
+ redeals[m_redeal]->setVisible(false);
+}
+
+//-------------------------------------------------------------------------//
+
+void Spider::restart()
+{
+ deck->collectAndShuffle();
+ deal();
+}
+
+//-------------------------------------------------------------------------//
+
+CardList Spider::getRun(Card *c) const
+{
+ CardList result;
+
+ Pile *p = c->source();
+ if (!p || p->isEmpty())
+ return result;
+
+ result.append(c);
+
+ Card::Suit s = c->suit();
+ int v = c->rank();
+
+ int index = p->indexOf(c);
+ c = p->at(--index);
+ while (index >= 0 && c->realFace() &&
+ c->suit() == s && c->rank() == ++v)
+ {
+ result.prepend(c);
+ c = p->at(--index);
+ }
+
+ return result;
+}
+
+void Spider::checkPileDeck(Pile *check)
+{
+ kdDebug(11111) << "check for run" << endl;
+ if (check->isEmpty())
+ return;
+
+ if (check->top()->rank() == Card::Ace) {
+ // just using the CardList to see if this goes to King
+ CardList run = getRun(check->top());
+ if (run.first()->rank() == Card::King) {
+ legs[m_leg]->setVisible(true);
+
+ // remove this full deck from this pile
+ CardList cl;
+ for (int i = 0; i < 13; i++ ) {
+ cl.append(check->cards().last());
+ check->moveCards(cl, legs[m_leg]);
+ cl.clear();
+ }
+ m_leg++;
+ }
+ }
+}
+
+void Spider::dealRow()
+{
+ if (m_redeal > 4)
+ return;
+
+ for (int column = 0; column < 10; column++) {
+ stack[column]->add(redeals[m_redeal]->top(), false, true);
+
+ // I may put an Ace on a K->2 pile so it could need cleared.
+ if (stack[column]->top()->rank() == Card::Ace)
+ checkPileDeck(stack[column]);
+ }
+
+ redeals[m_redeal++]->setVisible(false);
+}
+
+//-------------------------------------------------------------------------//
+
+void Spider::deal()
+{
+ unmarkAll();
+
+ m_leg = 0;
+ m_redeal = 0;
+
+ int column = 0;
+ // deal face down cards (5 to first 4 piles, 4 to last 6)
+ for (int i = 0; i < 44; i++ ) {
+ stack[column]->add(deck->nextCard(), true, true);
+ column = (column + 1) % 10;
+ }
+ // deal face up cards, one to each pile
+ for (int i = 0; i < 10; i++ ) {
+ stack[column]->add(deck->nextCard(), false, true);
+ column = (column + 1) % 10;
+ }
+ // deal the remaining cards into 5 'redeal' piles
+ for (int column = 0; column < 5; column++ )
+ for (int i = 0; i < 10; i++ )
+ redeals[column]->add(deck->nextCard(), true, false);
+
+ // make the leg piles invisible
+ for (int i = 0; i < 8; i++ )
+ legs[i]->setVisible(false);
+ // make the redeal piles visible
+ for (int i = 0; i < 5; i++ )
+ redeals[i]->setVisible(true);
+}
+
+Card *Spider::demoNewCards()
+{
+ if (m_leg > 4)
+ return 0;
+ deckClicked(0);
+ return stack[0]->top();
+}
+
+void Spider::deckClicked(Card*)
+{
+ kdDebug(11111) << "deck clicked " << m_redeal << endl;
+ if (m_redeal > 4)
+ return;
+
+ unmarkAll();
+ dealRow();
+ takeState();
+}
+
+bool Spider::isGameLost() const
+{
+ kdDebug(11111) << "isGameLost ?"<< endl;
+
+ // if there are still cards to deal out, you have not lost
+ if (m_redeal < 5)
+ return false;
+
+ // first, get runs from each stack - returning if empty
+ CardList cl[10];
+
+ for (int column = 0; column < 10; column++) {
+ if (stack[column]->isEmpty())
+ return false;
+ cl[column] = getRun(stack[column]->top());
+ }
+
+ // from this point on, I know that none of the columns is empty
+ // now check to see if a run from one column can go on the end
+ // of a run from another stack
+ for (int column = 0; column < 10; column++)
+ for (int c2 = 0; c2 < 10; c2++) {
+ if (c2 == column)
+ continue;
+
+ // if I can move this run to another pile, I'm not done
+ if (cl[c2].last()->rank() == cl[column].first()->rank()+1)
+ return false;
+ }
+
+ // if you can build a run from Ace->King in one suit then
+ // you can clear it and keep playing
+ for (int s = Card::Clubs; s <= Card::Spades; s++) {
+ bool bGrowing = true;
+ int vTop = 0;
+ while (bGrowing && vTop < 13) {
+ bGrowing = false;
+ int column = 0;
+ while (column < 10 && !bGrowing) {
+ if (cl[column].last()->suit() == s &&
+ cl[column].last()->rank() <= vTop+1 &&
+ cl[column].first()->rank() > vTop)
+ {
+ bGrowing = true;
+ vTop = cl[column].first()->rank();
+ }
+ column++;
+ }
+ }
+ // if you can build such a pile, you can continue
+ if (vTop == 13)
+ return false;
+ }
+
+ return true;
+}
+
+static class LocalDealerInfo15 : public DealerInfo
+{
+public:
+ LocalDealerInfo15() : DealerInfo(I18N_NOOP("S&pider (Easy)"), 14) {}
+ virtual Dealer *createGame(KMainWindow *parent) { return new Spider(1, parent); }
+} ldi15;
+
+static class LocalDealerInfo16 : public DealerInfo
+{
+public:
+ LocalDealerInfo16() : DealerInfo(I18N_NOOP("Spider (&Medium)"), 15) {}
+ virtual Dealer *createGame(KMainWindow *parent) { return new Spider(2, parent); }
+} ldi16;
+
+static class LocalDealerInfo17 : public DealerInfo
+{
+public:
+ LocalDealerInfo17() : DealerInfo(I18N_NOOP("Spider (&Hard)"), 16) {}
+ virtual Dealer *createGame(KMainWindow *parent) { return new Spider(4, parent); }
+} ldi17;
+
+//-------------------------------------------------------------------------//
+
+#include "spider.moc"
+
+//-------------------------------------------------------------------------//
+
diff --git a/kpat/spider.h b/kpat/spider.h
new file mode 100644
index 00000000..ea2cc165
--- /dev/null
+++ b/kpat/spider.h
@@ -0,0 +1,71 @@
+/*---------------------------------------------------------------------------
+
+ spider.cpp implements a patience card game
+
+ Copyright (C) 2003 Josh Metzler
+
+ * 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.
+
+---------------------------------------------------------------------------*/
+
+#ifndef _SPIDER_H_
+#define _SPIDER_H_
+
+#include "dealer.h"
+
+class SpiderPile : public Pile
+{
+public:
+ SpiderPile(int _index, Dealer* parent = 0) : Pile(_index, parent) {}
+ virtual void moveCards(CardList &c, Pile *to);
+ CardList getRun();
+};
+
+class Spider : public Dealer
+{
+ Q_OBJECT
+
+public:
+ Spider(int suits, KMainWindow *parent=0, const char *name=0);
+ void deal();
+ void dealRow();
+ void checkPileDeck(Pile *to);
+ virtual void restart();
+ virtual bool isGameLost() const;
+
+public slots:
+ void deckClicked(Card *c);
+
+protected:
+ virtual bool checkRemove(int /*checkIndex*/, const Pile *p, const Card *c) const;
+ virtual bool checkAdd(int /*checkIndex*/, const Pile *c1, const CardList &c2) const;
+ virtual QString getGameState() const;
+ virtual void setGameState(const QString &stream);
+ virtual void getHints();
+ virtual MoveHint *chooseHint();
+ virtual Card *demoNewCards();
+
+private:
+ CardList getRun(Card *c) const;
+
+ SpiderPile *stack[10];
+ Pile *legs[8];
+ int m_leg;
+ Pile *redeals[5];
+ int m_redeal;
+ Deck *deck;
+};
+
+#endif
+
+//-------------------------------------------------------------------------//
diff --git a/kpat/version.h b/kpat/version.h
new file mode 100644
index 00000000..b5eae065
--- /dev/null
+++ b/kpat/version.h
@@ -0,0 +1,6 @@
+#ifndef KPAT_VERSION
+#define KPAT_VERSION "2.2.2"
+#endif
+
+#define settings_group "General Settings"
+#define scores_group "Scores"
diff --git a/kpat/yukon.cpp b/kpat/yukon.cpp
new file mode 100644
index 00000000..859b3746
--- /dev/null
+++ b/kpat/yukon.cpp
@@ -0,0 +1,130 @@
+#include "yukon.h"
+#include <klocale.h>
+#include <kdebug.h>
+#include "deck.h"
+#include <assert.h>
+#include "cardmaps.h"
+
+Yukon::Yukon( KMainWindow* 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;
+
+ deck = Deck::new_deck(this);
+ deck->move(10, 10+dist_y*3);
+ deck->hide();
+
+ for (int i=0; i<4; i++) {
+ target[i] = new Pile(i+1, this);
+ target[i]->move(20+7*dist_x, 10+dist_y *i);
+ target[i]->setType(Pile::KlondikeTarget);
+ }
+
+ for (int i=0; i<7; i++) {
+ store[i] = new Pile(5+i, this);
+ store[i]->move(15+dist_x*i, 10);
+ store[i]->setAddType(Pile::KlondikeStore);
+ store[i]->setRemoveFlags(Pile::several | Pile::autoTurnTop);
+ }
+
+ setActions(Dealer::Hint | Dealer::Demo);
+}
+
+void Yukon::restart() {
+ deck->collectAndShuffle();
+ deal();
+}
+
+void Yukon::deal() {
+ for (int round = 0; round < 11; round++)
+ {
+ for (int j = 0; j < 7; j++)
+ {
+ bool doit = false;
+ switch (j) {
+ case 0:
+ doit = (round == 0);
+ break;
+ default:
+ doit = (round < j + 5);
+ }
+ if (doit)
+ store[j]->add(deck->nextCard(), round < j && j != 0, true);
+ }
+ }
+}
+
+bool Yukon::isGameLost() const {
+ int i,j,k,l,indexi,freeStore=0;
+ Card *c, *cNewTop;
+
+ kdDebug(11111) <<"isGameLost" << endl;
+
+ for(i=0; i < 7; i++){
+ if( store[i]->isEmpty() ){
+ freeStore++;
+ continue;
+ }
+
+ if(store[i]->top()->rank() == Card::Ace ||
+ ! store[i]->top()->isFaceUp())
+ return false;
+
+ for(indexi=store[i]->indexOf(store[i]->top()); indexi >=0; indexi--){
+
+ c=store[i]->at(indexi);
+ if( !c->isFaceUp() )
+ break;
+
+ if(freeStore > 0 && indexi > 0 && c->rank() == Card::King)
+ return false;
+
+ for(j=0; j < 4;j++){
+ if(!target[j]->isEmpty() &&
+ c->rank()-1 == target[j]->top()->rank() &&
+ c->suit() == target[j]->top()->suit())
+ return false;
+ }
+
+ for(j=1; j < 7; j++){
+ k=(i+j) % 7;
+ if( !store[k]->isEmpty() ) {
+ if(c->rank()+1 == store[k]->top()->rank() &&
+ (c->isRed() != store[k]->top()->isRed())){
+
+ if(indexi == 0)
+ return false;
+ else{
+ cNewTop=store[i]->at(indexi-1);
+ if(!cNewTop->isFaceUp())
+ return false;
+ if(cNewTop->rank() == Card::Ace)
+ return false;
+ if(cNewTop->rank() != store[k]->top()->rank() ||
+ cNewTop->isRed() != store[k]->top()->isRed())
+ return false;
+
+ for(l=0; l < 4;l++){
+ if(!target[l]->isEmpty() &&
+ cNewTop->rank()-1 == target[l]->top()->rank() &&
+ cNewTop->suit() == target[l]->top()->suit())
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return (freeStore!=7);
+}
+
+static class LocalDealerInfo10 : public DealerInfo
+{
+public:
+ LocalDealerInfo10() : DealerInfo(I18N_NOOP("&Yukon"), 10) {}
+ virtual Dealer *createGame(KMainWindow *parent) { return new Yukon(parent); }
+} gfi10;
+
+#include "yukon.moc"
diff --git a/kpat/yukon.h b/kpat/yukon.h
new file mode 100644
index 00000000..9b407ac3
--- /dev/null
+++ b/kpat/yukon.h
@@ -0,0 +1,23 @@
+#ifndef YUKON_H
+#define YUKON_H
+
+#include "dealer.h"
+
+class Yukon : public Dealer {
+ Q_OBJECT
+
+public:
+ Yukon( KMainWindow* parent=0, const char* name=0);
+ virtual bool isGameLost() const;
+
+public slots:
+ void deal();
+ virtual void restart();
+
+private:
+ Pile* store[7];
+ Pile* target[4];
+ Deck *deck;
+};
+
+#endif