summaryrefslogtreecommitdiffstats
path: root/kpat/freecell-solver
diff options
context:
space:
mode:
Diffstat (limited to 'kpat/freecell-solver')
-rw-r--r--kpat/freecell-solver/CREDITS57
-rw-r--r--kpat/freecell-solver/INSTALL70
-rw-r--r--kpat/freecell-solver/Makefile.am5
-rw-r--r--kpat/freecell-solver/Makefile.lite94
-rw-r--r--kpat/freecell-solver/README105
-rw-r--r--kpat/freecell-solver/USAGE518
-rw-r--r--kpat/freecell-solver/alloc.c127
-rw-r--r--kpat/freecell-solver/alloc.h86
-rw-r--r--kpat/freecell-solver/app_str.c74
-rw-r--r--kpat/freecell-solver/app_str.h39
-rw-r--r--kpat/freecell-solver/caas.c629
-rw-r--r--kpat/freecell-solver/caas.h28
-rw-r--r--kpat/freecell-solver/card.c286
-rw-r--r--kpat/freecell-solver/card.h100
-rw-r--r--kpat/freecell-solver/cl_chop.c245
-rw-r--r--kpat/freecell-solver/cl_chop.h19
-rw-r--r--kpat/freecell-solver/cmd_line.c964
-rw-r--r--kpat/freecell-solver/fcs.h797
-rw-r--r--kpat/freecell-solver/fcs_cl.h65
-rw-r--r--kpat/freecell-solver/fcs_config.h95
-rw-r--r--kpat/freecell-solver/fcs_dm.c146
-rw-r--r--kpat/freecell-solver/fcs_dm.h49
-rw-r--r--kpat/freecell-solver/fcs_enums.h77
-rw-r--r--kpat/freecell-solver/fcs_hash.c291
-rw-r--r--kpat/freecell-solver/fcs_hash.h102
-rw-r--r--kpat/freecell-solver/fcs_isa.c88
-rw-r--r--kpat/freecell-solver/fcs_isa.h56
-rw-r--r--kpat/freecell-solver/fcs_move.h122
-rw-r--r--kpat/freecell-solver/fcs_user.h275
-rw-r--r--kpat/freecell-solver/freecell.c2433
-rw-r--r--kpat/freecell-solver/inline.h20
-rw-r--r--kpat/freecell-solver/intrface.c1764
-rw-r--r--kpat/freecell-solver/jhjtypes.h25
-rw-r--r--kpat/freecell-solver/lib.c1244
-rw-r--r--kpat/freecell-solver/lookup2.c119
-rw-r--r--kpat/freecell-solver/lookup2.h13
-rw-r--r--kpat/freecell-solver/main.c859
-rw-r--r--kpat/freecell-solver/move.c531
-rw-r--r--kpat/freecell-solver/move.h172
-rw-r--r--kpat/freecell-solver/ms_ca.h33
-rw-r--r--kpat/freecell-solver/pqueue.c173
-rw-r--r--kpat/freecell-solver/pqueue.h71
-rw-r--r--kpat/freecell-solver/prefix.h4
-rw-r--r--kpat/freecell-solver/preset.c637
-rw-r--r--kpat/freecell-solver/preset.h62
-rw-r--r--kpat/freecell-solver/rand.c30
-rw-r--r--kpat/freecell-solver/rand.h49
-rw-r--r--kpat/freecell-solver/scans.c1170
-rw-r--r--kpat/freecell-solver/simpsim.c1716
-rw-r--r--kpat/freecell-solver/state.c1114
-rw-r--r--kpat/freecell-solver/state.h660
-rw-r--r--kpat/freecell-solver/test_arr.h136
-rw-r--r--kpat/freecell-solver/tests.h307
53 files changed, 18951 insertions, 0 deletions
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 */