6
votes

I am trying to solve Peg Solitaire with a depth-first search algorithm – it should be possible to solve the game since "modern computers can easily examine all game positions in a reasonable time". Even after 23 hours, the algorithm didn't find any solution. I did a web search and found the paper "Depth-first search solves peg solitaire". I tried the c-program from the paper and the first solution was found immediately after the program started. I compared the algorithms. The main difference between the algorithms is the way they find possible peg-jumps. While my algorithm searches the board for holes from top left to bottom right (each hole is checked if there are possible jumps), the paper-algorithm searches the board for pegs from top left to bottom right (each peg is checked if there are possible jumps). Both algorithms are trying the jumps in the order they are found. To underline the difference:

  • Analyzing holes: Runtime 23 hours no solution
  • Analyzing pegs: Runtime 10 seconds, 2940 solutions

Question: Why is there such a giant (not solvable / immediately solved) difference on how to search the board for jumps? Why is it better to check the pegs instead of checking the holes for possible jumps?

You can try the algorithm with following C++ program. To keep it compact I have removed the lesser important parts (printing the board, generating the initial bitboard, ...).

#include <iostream>
#include <ctime>
#include <vector>
#include <utility>
#include <ctime>

typedef unsigned __int64 ui64;
typedef std::vector<std::pair<int, int> > vecjumps; // first=from, second=to
ui64 moves = 0;    // Number of moves made so far
int solutions = 0; // Number of solutions found so far
clock_t start;     // To measure time

//          Bitboard
//   Board            Bits         
//  ------------------------------
//    ***           02 03 04      
//    ***           10 11 12      
//  *******   16 17 18 19 20 21 22
//  ***o***   24 25 26 27 28 29 30
//  *******   32 33 34 35 36 37 38
//    ***           42 43 44      
//    ***           50 51 52      
const ui64 bitboard = 0x001c1c7f7f7f1c1c; // 1ULL << 2 | 1ULL << 3 | ...
ui64 board = bitboard & ~(1ULL << 27);    // Initial Board: Bit 27 <=> Hole

// To try the hole-version: Swap Commented and Uncommented parts
vecjumps getMoves(ui64 b)
{
    // Find the possible jumps through bit-shift operations. mr <=> right jump
    // possibilities. The inverted Board represents the Holes. Shifting the
    // board by 16 right/left --> moving all pegs up/down. Shifting the board
    // by 1 --> moving all pegs left/right
    //ui64 mr = (((b >> 1) & b) <<  2) & ~b & bitboard;
    //ui64 md = (((b >> 8) & b) << 16) & ~b & bitboard;
    //ui64 ml = (((b >> 1) & b) >>  1) & ~b & bitboard;
    //ui64 mu = (((b >> 8) & b) >>  8) & ~b & bitboard;
    ui64 mr = ((((b >> 1) & b) <<  2) & ~b & bitboard) >>  2;
    ui64 md = ((((b >> 8) & b) << 16) & ~b & bitboard) >> 16;
    ui64 ml = ((((b >> 1) & b) >>  1) & ~b & bitboard) <<  2;
    ui64 mu = ((((b >> 8) & b) >>  8) & ~b & bitboard) << 16;

    vecjumps jumps;
    jumps.reserve(12);
    for (int i = 2; i < 53; i++)
    {
        //if (mr & (1ULL << i)) jumps.push_back(std::make_pair(i -  2, i));
        //if (md & (1ULL << i)) jumps.push_back(std::make_pair(i - 16, i));
        //if (ml & (1ULL << i)) jumps.push_back(std::make_pair(i +  2, i));
        //if (mu & (1ULL << i)) jumps.push_back(std::make_pair(i + 16, i));
        if (mr & (1ULL << i)) jumps.push_back(std::make_pair(i, i + 2));
        if (md & (1ULL << i)) jumps.push_back(std::make_pair(i, i + 16));
        if (ml & (1ULL << i)) jumps.push_back(std::make_pair(i, i - 2));
        if (mu & (1ULL << i)) jumps.push_back(std::make_pair(i, i - 16));
    }
    return jumps;
}

void makeMove(ui64& b, int from, int to)
{
    // Through a xor-instruction, a jump from 11 to 27 includes 19
    // 19 = (11 + 27) / 2
    b ^= 1ULL << from | 1ULL << (from + to) / 2 | 1ULL << to;
    moves++;
    if (moves % 10000000 == 0)
        printf("(%8d, %14llu moves, %lf)\n", 
            solutions, moves, (double)(clock() - start) / CLOCKS_PER_SEC);
}

// Depth First Search Algorithm
bool findSolution(int depth)
{
    if (!depth) return ((1ULL << 27) & board);
    vecjumps jumps = getMoves(board);
    for (vecjumps::const_iterator cit = jumps.begin(); cit != jumps.end();
         ++cit)
    {
        ui64 copy = board;
        makeMove(board, (*cit).first, (*cit).second);
        if (findSolution(depth - 1)) solutions++;
        board = copy;
    }
    return false;
}

int main()
{
    start = clock();
    findSolution(31);   
    printf("(%8d, %14llu moves, %lf)\n", 
        solutions, moves, (double)(clock() - start) / CLOCKS_PER_SEC);
    return 0;
}
1
As a wild guess, your alg seems to work extensively with bits. So, it makes sense to consider 'Bit complexity' of both approaches. May be, working time depends on the number of set(unset) bits on board. I didn't do analysis, but maybe, faster approach generates less set (or unset) bits, and, thus, faster?Victor Sorokin
@Victor: I don't think its the bit-complexity, there are more bit-operations on the faster approach. If we compare the number of moves needed to find the first solution: The peg-version only needs ~20300 moves, the hole-version didn't find any solution, even after hundred-millions of moves. I think, the reason must lie in the order the moves are tried. If so, why is the peg-ordering better then the hole ordering?Christian Ammer

1 Answers

1
votes

If there are no loops in the resulting tree in either method, and the resulting tree is the same, the reason for the hudge difference when aplying the same search algorithm must be the order in which the nodes are expanded (heuristic). I'm still checking your implementation but everything seems right on it, so I can't see any other reason for the speed difference.

Some time ago I had an assigment on this problem, and I found this on my bookmarks: You can read more info, depth search vs breadth search and a couple of heuristics to improve the search here: http://www.durangobill.com/Peg33.html