Contents Up Previous Next

Freecell Solver - Evolution of a C Program


Contents Up Previous Next

Contents Up Previous Next

1 Introduction


Contents Up Previous Next

Contents Up Previous Next

1.1 Rules of the Game


Contents Up Previous Next

Contents Up Previous Next

1.1.1 Common Strategies


Contents Up Previous Next

Contents Up Previous Next

1.2 Copyrights and Disclaimer

Freecell Solver is distributed under the public domain. This lecture, on the other hand, is a copyrighted, non free-content work. All rights are reserved.

I hereby disclaim any damage that reading this work may cause you, either implicit or explicit, direct or indirect.


Contents Up Previous Next

Contents Up Previous Next

2 Freecell Solver 0.2's Architecture


Contents Up Previous Next

Contents Up Previous Next

2.1 The Scan that was used

Pseudo-Code

Solve-State(state, prev_states, ret)
    if (state == empty_state)
        Push(ret, state)
        return SOLVED
    for each move possible on state
        new_state <- apply(state, move)
        if (new_state in prev_states)
            ; Do nothing
        else
            add new_state to prev_states
            if (Solve-State(new_state, prev_states, ret) == SOLVED)
                Push(ret, state)
                return SOLVED
    return UNSOLVED

Freecell-Solver(init_state)
    if (Solve-State(init_state, Null, ret) == SOLVED)
        print "State was solved"
        while (state <- Pop(ret))
            print state
    else
        print "I could not solve this board";

  • It's actually a Depth-First Search (DFS) scan.

  • Contents Up Previous Next

    Contents Up Previous Next

    3 Evolution of the States' Collection

  • In order to search efficiently we need to keep a collection of states which we encountered so far.
  • That is so they (and their derived states) won't be traversed to again and again.
  • But how do we manage this collection?

  • Contents Up Previous Next

    Contents Up Previous Next

    3.1 Initial Perl Version - Flat List


    Contents Up Previous Next

    Contents Up Previous Next

    3.2 First C Version - Sorted Array

  • I kept a sorted array of states.
  • New states were added to the end of the array, which was kept momentarily unsorted.
  • After a few of them were collected, they were added to the main array using qsort.
  • O(log(n)) lookup and O(n*log(n)) - O(n2) addition.
  • Reasonable performance for most boards.

  • Contents Up Previous Next

    Contents Up Previous Next

    3.3 Binary-Search-Based Merge to Add the Sort Margin


    Contents Up Previous Next

    Contents Up Previous Next

    3.4 A Balanced Binary Tree


    Contents Up Previous Next

    Contents Up Previous Next

    3.5 A Hash Table

    Can we do better than O(n*log(n))?

    What is a hash?

  • As far as we are concerned a hash is an array of buckets. The index of the bucket into which a given state goes is determined according to a deterministic hash function.
  • A hash function is highly magical and should make sure similar records have very distinct hash values, and all hash values are generated in roughly equal frequency.

  • Contents Up Previous Next

    Contents Up Previous Next

    3.5.1 The Choice of a Hash Function


    Contents Up Previous Next

    Contents Up Previous Next

    3.5.2 Hash Optimizations


    Contents Up Previous Next

    Contents Up Previous Next

    3.6 Benchmarks

    FILL IN


    Contents Up Previous Next

    Contents Up Previous Next

    4 Moves Management


    Contents Up Previous Next

    Contents Up Previous Next

    4.1 Meta-Moves (instead of Atomic ones)


    Contents Up Previous Next

    Contents Up Previous Next

    4.2 Stack to Stack Moves


    Contents Up Previous Next

    Contents Up Previous Next

    4.3 More Moves Generalization


    Contents Up Previous Next

    Contents Up Previous Next

    4.4 Non-Solvable Deals


    Contents Up Previous Next

    Contents Up Previous Next

    5 Scanning


    Contents Up Previous Next

    Contents Up Previous Next

    5.1 Specifying the Order of Tests


    Contents Up Previous Next

    Contents Up Previous Next

    5.2 Best-First Search


    Contents Up Previous Next

    Contents Up Previous Next

    5.3 Soft DFS


    Contents Up Previous Next

    Contents Up Previous Next

    5.4 The BFS Optimization Scan


    Contents Up Previous Next

    Contents Up Previous Next

    6 The State Representation


    Contents Up Previous Next

    Contents Up Previous Next

    6.1 Reducing the Data Type Bit-Width


    Contents Up Previous Next

    Contents Up Previous Next

    6.2 Pointers to Stacks


    Contents Up Previous Next

    Contents Up Previous Next

    6.3 Remembering the Original Stack and Freecell Locations


    Contents Up Previous Next

    Contents Up Previous Next

    6.3.1 Solution


    Contents Up Previous Next

    Contents Up Previous Next

    7 Board Auto-Generators


    Contents Up Previous Next

    Contents Up Previous Next

    8 Why not C++?

    Markus Oberhumer (of PySol fame asked if I thought about converting Freecell Solver to C++. (I suppose he meant with STL, templates and all). Here is a full answer why I'm not going to do it:

    1. The solver is already working in ANSI C.
    2. The code is not overly object-oriented. Whatever OOP concepts exist there. fit well enough within what ANSI C gives me.
    3. C++/STL may make it slower, perhaps even considerably. I'd rather not spend time risking something like that, only to roll it back later.
    4. ANSI C compiles much faster. (at least with gcc)
    5. ANSI C is cleaner, more portable and causes less unexpected problems than C++.

    I'm more willing to integrate C++-derived features there into the ANSI C code. Things like namespaces, (non-inherited) classes, or inline functions. However, for the time being, I'd like to maintain the code as pure ANSI C.

    For that matter, some of the gcc extensions can prove very useful too, but I cannot use them either.


    Contents Up Previous Next

    Contents Up Previous Next

    9 The fc-solve-discuss flame-war


    Contents Up Previous Next

    Contents Up Previous Next

    10 The story of the user API


    Contents Up Previous Next

    Contents Up Previous Next

    11 Auto-confisication and Friends


    Contents Up Previous Next

    Contents Up Previous Next

    12 The Freshmeat Effect (and how to avoid it)


    Contents Up Previous Next