diff options
Diffstat (limited to 'kpat')
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>&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 Binary files differnew file mode 100644 index 00000000..26793078 --- /dev/null +++ b/kpat/green.png 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 Binary files differnew file mode 100644 index 00000000..5bb5cdc7 --- /dev/null +++ b/kpat/icons/hi128-app-kpat.png diff --git a/kpat/icons/hi16-app-kpat.png b/kpat/icons/hi16-app-kpat.png Binary files differnew file mode 100644 index 00000000..a33e861c --- /dev/null +++ b/kpat/icons/hi16-app-kpat.png diff --git a/kpat/icons/hi22-app-kpat.png b/kpat/icons/hi22-app-kpat.png Binary files differnew file mode 100644 index 00000000..529d5de5 --- /dev/null +++ b/kpat/icons/hi22-app-kpat.png diff --git a/kpat/icons/hi32-app-kpat.png b/kpat/icons/hi32-app-kpat.png Binary files differnew file mode 100644 index 00000000..b5424928 --- /dev/null +++ b/kpat/icons/hi32-app-kpat.png diff --git a/kpat/icons/hi48-app-kpat.png b/kpat/icons/hi48-app-kpat.png Binary files differnew file mode 100644 index 00000000..41ea79b4 --- /dev/null +++ b/kpat/icons/hi48-app-kpat.png diff --git a/kpat/icons/hi64-app-kpat.png b/kpat/icons/hi64-app-kpat.png Binary files differnew file mode 100644 index 00000000..0ec12569 --- /dev/null +++ b/kpat/icons/hi64-app-kpat.png diff --git a/kpat/icons/kpat-lq.png b/kpat/icons/kpat-lq.png Binary files differnew file mode 100644 index 00000000..8ea647d2 --- /dev/null +++ b/kpat/icons/kpat-lq.png 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>&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>&Edit</text> + <Action name="undo_move"/> + </Menu> + <Menu name="settings" noMerge="1"><text>&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 |