From c90c389a8a8d9d8661e9772ec4144c5cf2039f23 Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdegames@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- kpat/freecell-solver/CREDITS | 57 + kpat/freecell-solver/INSTALL | 70 ++ kpat/freecell-solver/Makefile.am | 5 + kpat/freecell-solver/Makefile.lite | 94 ++ kpat/freecell-solver/README | 105 ++ kpat/freecell-solver/USAGE | 518 ++++++++ kpat/freecell-solver/alloc.c | 127 ++ kpat/freecell-solver/alloc.h | 86 ++ kpat/freecell-solver/app_str.c | 74 ++ kpat/freecell-solver/app_str.h | 39 + kpat/freecell-solver/caas.c | 629 ++++++++++ kpat/freecell-solver/caas.h | 28 + kpat/freecell-solver/card.c | 286 +++++ kpat/freecell-solver/card.h | 100 ++ kpat/freecell-solver/cl_chop.c | 245 ++++ kpat/freecell-solver/cl_chop.h | 19 + kpat/freecell-solver/cmd_line.c | 964 ++++++++++++++ kpat/freecell-solver/fcs.h | 797 ++++++++++++ kpat/freecell-solver/fcs_cl.h | 65 + kpat/freecell-solver/fcs_config.h | 95 ++ kpat/freecell-solver/fcs_dm.c | 146 +++ kpat/freecell-solver/fcs_dm.h | 49 + kpat/freecell-solver/fcs_enums.h | 77 ++ kpat/freecell-solver/fcs_hash.c | 291 +++++ kpat/freecell-solver/fcs_hash.h | 102 ++ kpat/freecell-solver/fcs_isa.c | 88 ++ kpat/freecell-solver/fcs_isa.h | 56 + kpat/freecell-solver/fcs_move.h | 122 ++ kpat/freecell-solver/fcs_user.h | 275 ++++ kpat/freecell-solver/freecell.c | 2433 ++++++++++++++++++++++++++++++++++++ kpat/freecell-solver/inline.h | 20 + kpat/freecell-solver/intrface.c | 1764 ++++++++++++++++++++++++++ kpat/freecell-solver/jhjtypes.h | 25 + kpat/freecell-solver/lib.c | 1244 ++++++++++++++++++ kpat/freecell-solver/lookup2.c | 119 ++ kpat/freecell-solver/lookup2.h | 13 + kpat/freecell-solver/main.c | 859 +++++++++++++ kpat/freecell-solver/move.c | 531 ++++++++ kpat/freecell-solver/move.h | 172 +++ kpat/freecell-solver/ms_ca.h | 33 + kpat/freecell-solver/pqueue.c | 173 +++ kpat/freecell-solver/pqueue.h | 71 ++ kpat/freecell-solver/prefix.h | 4 + kpat/freecell-solver/preset.c | 637 ++++++++++ kpat/freecell-solver/preset.h | 62 + kpat/freecell-solver/rand.c | 30 + kpat/freecell-solver/rand.h | 49 + kpat/freecell-solver/scans.c | 1170 +++++++++++++++++ kpat/freecell-solver/simpsim.c | 1716 +++++++++++++++++++++++++ kpat/freecell-solver/state.c | 1114 +++++++++++++++++ kpat/freecell-solver/state.h | 660 ++++++++++ kpat/freecell-solver/test_arr.h | 136 ++ kpat/freecell-solver/tests.h | 307 +++++ 53 files changed, 18951 insertions(+) create mode 100644 kpat/freecell-solver/CREDITS create mode 100644 kpat/freecell-solver/INSTALL create mode 100644 kpat/freecell-solver/Makefile.am create mode 100644 kpat/freecell-solver/Makefile.lite create mode 100644 kpat/freecell-solver/README create mode 100644 kpat/freecell-solver/USAGE create mode 100644 kpat/freecell-solver/alloc.c create mode 100644 kpat/freecell-solver/alloc.h create mode 100644 kpat/freecell-solver/app_str.c create mode 100644 kpat/freecell-solver/app_str.h create mode 100644 kpat/freecell-solver/caas.c create mode 100644 kpat/freecell-solver/caas.h create mode 100644 kpat/freecell-solver/card.c create mode 100644 kpat/freecell-solver/card.h create mode 100644 kpat/freecell-solver/cl_chop.c create mode 100644 kpat/freecell-solver/cl_chop.h create mode 100644 kpat/freecell-solver/cmd_line.c create mode 100644 kpat/freecell-solver/fcs.h create mode 100644 kpat/freecell-solver/fcs_cl.h create mode 100644 kpat/freecell-solver/fcs_config.h create mode 100644 kpat/freecell-solver/fcs_dm.c create mode 100644 kpat/freecell-solver/fcs_dm.h create mode 100644 kpat/freecell-solver/fcs_enums.h create mode 100644 kpat/freecell-solver/fcs_hash.c create mode 100644 kpat/freecell-solver/fcs_hash.h create mode 100644 kpat/freecell-solver/fcs_isa.c create mode 100644 kpat/freecell-solver/fcs_isa.h create mode 100644 kpat/freecell-solver/fcs_move.h create mode 100644 kpat/freecell-solver/fcs_user.h create mode 100644 kpat/freecell-solver/freecell.c create mode 100644 kpat/freecell-solver/inline.h create mode 100644 kpat/freecell-solver/intrface.c create mode 100644 kpat/freecell-solver/jhjtypes.h create mode 100644 kpat/freecell-solver/lib.c create mode 100644 kpat/freecell-solver/lookup2.c create mode 100644 kpat/freecell-solver/lookup2.h create mode 100644 kpat/freecell-solver/main.c create mode 100644 kpat/freecell-solver/move.c create mode 100644 kpat/freecell-solver/move.h create mode 100644 kpat/freecell-solver/ms_ca.h create mode 100644 kpat/freecell-solver/pqueue.c create mode 100644 kpat/freecell-solver/pqueue.h create mode 100644 kpat/freecell-solver/prefix.h create mode 100644 kpat/freecell-solver/preset.c create mode 100644 kpat/freecell-solver/preset.h create mode 100644 kpat/freecell-solver/rand.c create mode 100644 kpat/freecell-solver/rand.h create mode 100644 kpat/freecell-solver/scans.c create mode 100644 kpat/freecell-solver/simpsim.c create mode 100644 kpat/freecell-solver/state.c create mode 100644 kpat/freecell-solver/state.h create mode 100644 kpat/freecell-solver/test_arr.h create mode 100644 kpat/freecell-solver/tests.h (limited to 'kpat/freecell-solver') 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.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 +#include + +#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;anum_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;packnum_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 +#include +#include +#include + +#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 +#include +#include +#include + +#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 +#include +#include + +#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 ; astacks_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 + +#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 +#include +#include + +#include "cl_chop.h" + +#ifdef DMALLOC +#include +#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;aargc;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;aargc;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 +#include +#include +#include + +#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 '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 + +#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 + +#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 + +/* #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 + +#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 +#include +#include +#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 +#include + +#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 + + +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 +#include + +#define DEBUG + +#ifdef DEBUG +#include +#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;isize;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;ientries[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 +#include + +#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;anum_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;pnum_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 +#include +#include +#include +#include + + +#include "fcs_config.h" + +#if FCS_STATE_STORAGE==FCS_STATE_STORAGE_LIBREDBLACK_TREE +#include +#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;stackdecks_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;fcdecks_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 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 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 ; afreecells_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= 0 ; c--) + { + card = fcs_stack_card(state, stack, c); + for(deck=0;deckdecks_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 ; afreecells_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;stackempty_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;bdc) + { + if (a < freecells_to_fill) + { + for(b=0;bfreecells_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 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 ; aempty_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;stackempty_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;dsempty_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; aempty_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;fcempty_stacks_fill == FCS_ES_FILLED_BY_KINGS_ONLY) ? + (fcs_card_card_num(card) == 13) : + (fcs_card_card_num(card) != 0) + ) + { + for(stack=0;stackfreecells_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 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 ; aempty_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;stackstacks_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;atalon_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 ; ss)+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_idxempty_stacks_fill; + + if (empty_stacks_filled_by == FCS_ES_FILLED_BY_NONE) + { + return FCS_STATE_IS_NOT_SOLVEABLE; + } + + + + for(stack=0;stack 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 0) + { + card = fcs_stack_card(state, stack, cards_num-1); + + for(ds=0;ds 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 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 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 +#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_idxnum_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;depthderived_states_list.states); + free(info_ptr->derived_states_random_indexes); + info_ptr++; + } + for(;depthderived_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;atests_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;atests_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_idxnum_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;astacks_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_idxnum_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 +#include +#include + +#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;inum_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;inum_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 +#include +#include + +#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 +#include +#include +#include + +#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 +#include +#include + +#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= 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 + +#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 +#include + +#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 + +#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 +#include + +#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= '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;(amax_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 + +#ifdef DMALLOC +#include +#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 +#include +#include +#include +#include + +#include "fcs_config.h" + +/* So FCS_STATE_STORAGE and friends would be defined */ +#if FCS_STATE_STORAGE==FCS_STATE_STORAGE_LIBREDBLACK_TREE +#include +#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;afinal_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_indexvisited & + 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 ; dsoft_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;afinal_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;astacks_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= 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;aempty_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)<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;adebug_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 + +#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= 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 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 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 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 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 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 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 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_indexstacks_num; + + for(stack=0;stack 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 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 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_indexstacks_num; + + for(stack=0;stack 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 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 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_indexstacks_num; + + for(stack=0;stack 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 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 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_indexstacks_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 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 +#include +#include + +#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 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;b0) && + (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;b0) && + (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;b0) && + (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;b0) && + (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;b0) && + ( +#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;b0) && + (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;astack_locs[a] = a; + } +#ifdef INDIRECT_STACK_STATES + for(a=0;as.stacks[a] = &indirect_stacks_buffer[a << 7]; + memset(state->s.stacks[a], '\0', MAX_NUM_DECKS*52+1); + } + for(;as.stacks[a] = NULL; + } +#endif + for(a=0;afc_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= 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;ds)); + + if (canonized_order_output) + { + for(a=0;astack_locs[a])] = a; + } + for(a=0;afc_locs[a])] = a; + } + } + + for(a=0;a max_num_cards) + { + max_num_cards = fcs_stack_len(*state, stack_locs[s]); + } + } + + for(card_num=0;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(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 +#include +#include + +#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 */ -- cgit v1.2.1