diff --git a/cpp/15puzzle b/cpp/15puzzle new file mode 100755 index 0000000..3a1948b Binary files /dev/null and b/cpp/15puzzle differ diff --git a/cpp/15puzzle.cpp b/cpp/15puzzle.cpp new file mode 100755 index 0000000..77ac495 --- /dev/null +++ b/cpp/15puzzle.cpp @@ -0,0 +1,742 @@ +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// STL A* Search implementation +// (C)2001 Justin Heyes-Jones +// +// This uses my A* code to solve the 8-puzzle + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include + +using namespace std; + +// Configuration + +#define NUM_TIMES_TO_RUN_SEARCH 1 +#define DISPLAY_SOLUTION_FORWARDS 1 +#define DISPLAY_SOLUTION_BACKWARDS 0 +#define DISPLAY_SOLUTION_INFO 1 +#define DEBUG_LISTS 1 + +// AStar search class +#include "stlastar.h" // See header for copyright and usage information + +// Global data + +#define BOARD_WIDTH (4) +#define BOARD_HEIGHT (4) + +#define GM_TILE (-1) +#define GM_SPACE (0) +#define GM_OFF_BOARD (1) + +// Definitions + +// To use the search class you must define the following calls... + +// Data +// Your own state space information +// Functions +// (Optional) Constructor. +// Nodes are created by the user, so whether you use a +// constructor with parameters as below, or just set the object up after the +// constructor, is up to you. +// +// (Optional) Destructor. +// The destructor will be called if you create one. You +// can rely on the default constructor unless you dynamically allocate something in +// your data +// +// float GoalDistanceEstimate( PuzzleState &nodeGoal ); +// Return the estimated cost to goal from this node (pass reference to goal node) +// +// bool IsGoal( PuzzleState &nodeGoal ); +// Return true if this node is the goal. +// +// bool GetSuccessors( AStarSearch *astarsearch ); +// For each successor to this state call the AStarSearch's AddSuccessor call to +// add each one to the current search - return false if you are out of memory and the search +// will fail +// +// float GetCost( PuzzleState *successor ); +// Return the cost moving from this state to the state of successor +// +// bool IsSameState( PuzzleState &rhs ); +// Return true if the provided state is the same as this state + +// Here the example is the 8-puzzle state ... +class PuzzleState +{ + +public: + + // defs + + typedef enum + { + TL_SPACE, + TL_1, + TL_2, + TL_3, + + TL_4, + TL_5, + TL_6, + TL_7, + + TL_8, + TL_9, + TL_10, + TL_11, + + TL_12, + TL_13, + TL_14, + TL_15, + } TILE; + + // data + + static TILE g_goal[ BOARD_WIDTH*BOARD_HEIGHT]; + static TILE g_start[ BOARD_WIDTH*BOARD_HEIGHT]; + + // the tile data for the 8-puzzle + TILE tiles[ BOARD_WIDTH*BOARD_HEIGHT ]; + + // member functions + + PuzzleState() { + memcpy( tiles, g_goal, sizeof( TILE ) * BOARD_WIDTH * BOARD_HEIGHT ); + } + + PuzzleState( TILE *param_tiles ) + { + memcpy( tiles, param_tiles, sizeof( TILE ) * BOARD_WIDTH * BOARD_HEIGHT ); + } + + float GoalDistanceEstimate( PuzzleState &nodeGoal ); + bool IsGoal( PuzzleState &nodeGoal ); + bool GetSuccessors( AStarSearch *astarsearch, PuzzleState *parent_node ); + float GetCost( PuzzleState &successor ); + bool IsSameState( PuzzleState &rhs ); + + void PrintNodeInfo(); + +private: + // User stuff - Just add what you need to help you write the above functions... + + void GetSpacePosition( PuzzleState *pn, int *rx, int *ry ); + bool LegalMove( TILE *StartTiles, TILE *TargetTiles, int spx, int spy, int tx, int ty ); + int GetMap( int x, int y, TILE *tiles ); +}; + +// Goal state +PuzzleState::TILE PuzzleState::g_goal[] = +{ + + TL_SPACE, + TL_1, + TL_2, + TL_3, + + TL_4, + TL_5, + TL_6, + TL_7, + + TL_8, + TL_9, + TL_10, + TL_11, + + TL_12, + TL_13, + TL_14, + TL_15, +}; + +PuzzleState::TILE PuzzleState::g_start[] = +{ + +#if 0 + TL_10, + TL_2, + TL_7, + TL_1, + TL_14, + TL_SPACE, + TL_8, + TL_4, + TL_13, + TL_5, + TL_15, + TL_12, + TL_9, + TL_11, + TL_3, + TL_6, + +#elif 1 // ass + TL_4, + TL_1, + TL_2, + TL_3, + TL_SPACE, + TL_5, + TL_6, + TL_7, + TL_8, + TL_9, + TL_10, + TL_11, + TL_12, + TL_13, + TL_14, + TL_15, +#elif 0 // works + TL_1, + TL_SPACE, + TL_2, + TL_3, + TL_4, + TL_5, + TL_6, + TL_7, + TL_8, + TL_9, + TL_10, + TL_11, + TL_12, + TL_13, + TL_14, + TL_15, + +#endif + +}; + +bool PuzzleState::IsSameState( PuzzleState &rhs ) +{ + + for( int i=0; i<(BOARD_HEIGHT*BOARD_WIDTH); i++ ) + { + if( tiles[i] != rhs.tiles[i] ) + { + return false; + } + } + + return true; + +} + +void PuzzleState::PrintNodeInfo() +{ + char str[1024]; + sprintf( str, "%20d %20d %20d %20d\n%20d %20d %20d %20d\n%20d %20d %20d %20d\n%20d %20d %20d %20d\n", + tiles[0], + tiles[1], + tiles[2], + tiles[3], + tiles[4], + tiles[5], + tiles[6], + tiles[7], + tiles[8], + tiles[9], + tiles[10], + tiles[11], + tiles[12], + tiles[13], + tiles[14], + tiles[15] + ); + + cout << str; +} + +// Here's the heuristic function that estimates the distance from a PuzzleState +// to the Goal. + +float PuzzleState::GoalDistanceEstimate( PuzzleState &nodeGoal ) +{ + + // Return the manhattan distance + + float cost = 0.0f; + int r,c; + + for(r=0; rtiles[(y*BOARD_WIDTH)+x] == TL_SPACE ) + { + *rx = x; + *ry = y; + + return; + } + } + } + + assert( false && "Something went wrong. There's no space on the board" ); + +} + +int PuzzleState::GetMap( int x, int y, TILE *tiles ) +{ + + if( x < 0 || + x >= BOARD_WIDTH || + y < 0 || + y >= BOARD_HEIGHT + ) + return GM_OFF_BOARD; + + if( tiles[(y*BOARD_WIDTH)+x] == TL_SPACE ) + { + return GM_SPACE; + } + + return GM_TILE; +} + +// Given a node set of tiles and a set of tiles to move them into, do the move as if it was on a tile board +// note : returns false if the board wasn't changed, and simply returns the tiles as they were in the target +// spx and spy is the space position while tx and ty is the target move from position + +bool PuzzleState::LegalMove( TILE *StartTiles, TILE *TargetTiles, int spx, int spy, int tx, int ty ) +{ + + int t; + + if( GetMap( spx, spy, StartTiles ) == GM_SPACE ) + { + if( GetMap( tx, ty, StartTiles ) == GM_TILE ) + { + + // copy tiles + for( t=0; t<(BOARD_HEIGHT*BOARD_WIDTH); t++ ) + { + TargetTiles[t] = StartTiles[t]; + } + + + TargetTiles[ (ty*BOARD_WIDTH)+tx ] = StartTiles[ (spy*BOARD_WIDTH)+spx ]; + TargetTiles[ (spy*BOARD_WIDTH)+spx ] = StartTiles[ (ty*BOARD_WIDTH)+tx ]; + + return true; + } + } + + + return false; + +} + +// This generates the successors to the given PuzzleState. It uses a helper function called +// AddSuccessor to give the successors to the AStar class. The A* specific initialisation +// is done for each node internally, so here you just set the state information that +// is specific to the application +bool PuzzleState::GetSuccessors( AStarSearch *astarsearch, PuzzleState *parent_node ) +{ + PuzzleState NewNode; + + int sp_x,sp_y; + + GetSpacePosition( this, &sp_x, &sp_y ); + + bool ret; + + if( LegalMove( tiles, NewNode.tiles, sp_x, sp_y, sp_x, sp_y-1 ) == true ) + { + ret = astarsearch->AddSuccessor( NewNode ); + + if( !ret ) return false; + } + + if( LegalMove( tiles, NewNode.tiles, sp_x, sp_y, sp_x, sp_y+1 ) == true ) + { + ret = astarsearch->AddSuccessor( NewNode ); + + if( !ret ) return false; + } + + if( LegalMove( tiles, NewNode.tiles, sp_x, sp_y, sp_x-1, sp_y ) == true ) + { + ret = astarsearch->AddSuccessor( NewNode ); + + if( !ret ) return false; + } + + if( LegalMove( tiles, NewNode.tiles, sp_x, sp_y, sp_x+1, sp_y ) == true ) + { + ret = astarsearch->AddSuccessor( NewNode ); + + if( !ret ) return false; + } + + return true; +} + +// given this node, what does it cost to move to successor. In the case +// of our map the answer is the map terrain value at this node since that is +// conceptually where we're moving + +float PuzzleState::GetCost( PuzzleState &successor ) +{ + return 1.0f; // I love it when life is simple + +} + + +// Main + +int main( int argc, char *argv[] ) +{ + + cout << "STL A* 8-puzzle solver implementation\n(C)2001 Justin Heyes-Jones\n"; + + if( argc > 1 ) + { + int i = 0; + int c; + + while( (c = argv[1][i]) ) + { + if( isdigit( c ) ) + { + int num = (c - '0'); + + PuzzleState::g_start[i] = static_cast(num); + + } + + i++; + } + + + } + + // Create an instance of the search class... + + AStarSearch astarsearch; + + int NumTimesToSearch = NUM_TIMES_TO_RUN_SEARCH; + + while( NumTimesToSearch-- ) + { + + // Create a start state + PuzzleState nodeStart( PuzzleState::g_start ); + + // Define the goal state + PuzzleState nodeEnd( PuzzleState::g_goal ); + + // Set Start and goal states + astarsearch.SetStartAndGoalStates( nodeStart, nodeEnd ); + + ((PuzzleState *)PuzzleState::g_start)->PrintNodeInfo(); + + unsigned int SearchState; + + unsigned int SearchSteps = 0; + + do + { + SearchState = astarsearch.SearchStep(); + +#if DEBUG_LISTS + + float f,g,h; + + cout << "Search step " << SearchSteps << endl; + + cout << "Open:\n"; + PuzzleState *p = astarsearch.GetOpenListStart( f,g,h ); + while( p ) + { + ((PuzzleState *)p)->PrintNodeInfo(); + cout << "f: " << f << " g: " << g << " h: " << h << "\n\n"; + + p = astarsearch.GetOpenListNext( f,g,h ); + + } + + cout << "Closed:\n"; + p = astarsearch.GetClosedListStart( f,g,h ); + while( p ) + { + p->PrintNodeInfo(); + cout << "f: " << f << " g: " << g << " h: " << h << "\n\n"; + + p = astarsearch.GetClosedListNext( f,g,h ); + } + +#endif + +// Test cancel search +#if 0 + int StepCount = astarsearch.GetStepCount(); + if( StepCount == 10 ) + { + astarsearch.CancelSearch(); + } +#endif + SearchSteps++; + getchar(); + } + while( SearchState == AStarSearch::SEARCH_STATE_SEARCHING ); + + if( SearchState == AStarSearch::SEARCH_STATE_SUCCEEDED ) + { +#if DISPLAY_SOLUTION_FORWARDS + cout << "Search found goal state\n"; +#endif + PuzzleState *node = astarsearch.GetSolutionStart(); + +#if DISPLAY_SOLUTION_FORWARDS + cout << "Displaying solution\n"; +#endif + int steps = 0; + +#if DISPLAY_SOLUTION_FORWARDS + node->PrintNodeInfo(); + cout << endl; +#endif + for( ;; ) + { + node = astarsearch.GetSolutionNext(); + + if( !node ) + { + break; + } + +#if DISPLAY_SOLUTION_FORWARDS + node->PrintNodeInfo(); + cout << endl; +#endif + steps ++; + + }; + +#if DISPLAY_SOLUTION_FORWARDS + // todo move step count into main algorithm + cout << "Solution steps " << steps << endl; +#endif + +//////////// + + node = astarsearch.GetSolutionEnd(); + +#if DISPLAY_SOLUTION_BACKWARDS + cout << "Displaying reverse solution\n"; +#endif + steps = 0; + + node->PrintNodeInfo(); + cout << endl; + for( ;; ) + { + node = astarsearch.GetSolutionPrev(); + + if( !node ) + { + break; + } +#if DISPLAY_SOLUTION_BACKWARDS + node->PrintNodeInfo(); + cout << endl; +#endif + steps ++; + + }; + +#if DISPLAY_SOLUTION_BACKWARDS + cout << "Solution steps " << steps << endl; +#endif + +////////////// + + // Once you're done with the solution you can free the nodes up + astarsearch.FreeSolutionNodes(); + + } + else if( SearchState == AStarSearch::SEARCH_STATE_FAILED ) + { +#if DISPLAY_SOLUTION_INFO + cout << "Search terminated. Did not find goal state\n"; +#endif + } + else if( SearchState == AStarSearch::SEARCH_STATE_OUT_OF_MEMORY ) + { +#if DISPLAY_SOLUTION_INFO + cout << "Search terminated. Out of memory\n"; +#endif + } + + + + // Display the number of loops the search went through +#if DISPLAY_SOLUTION_INFO + cout << "SearchSteps : " << astarsearch.GetStepCount() << endl; +#endif + } + + return 0; +} + + diff --git a/cpp/makefile b/cpp/makefile index 153a494..9f26ba5 100644 --- a/cpp/makefile +++ b/cpp/makefile @@ -1,7 +1,7 @@ -all : 8puzzle findpath minpathbucharest tests +all : 8puzzle findpath minpathbucharest tests 15puzzle clean : - rm 8puzzle findpath minpathbucharest tests + rm 8puzzle findpath minpathbucharest tests 15puzzle minpathbucharest : min_path_to_Bucharest.cpp stlastar.h g++ -Wall min_path_to_Bucharest.cpp -o minpathbucharest @@ -9,6 +9,9 @@ minpathbucharest : min_path_to_Bucharest.cpp stlastar.h 8puzzle : 8puzzle.cpp stlastar.h g++ -Wall 8puzzle.cpp -o 8puzzle +15puzzle : 15puzzle.cpp stlastar.h + g++ -Wall 15puzzle.cpp -o 15puzzle + findpath : findpath.cpp stlastar.h g++ -Wall findpath.cpp -o findpath diff --git a/cpp/stlastar.h b/cpp/stlastar.h index 04ab3c4..cab38cf 100755 --- a/cpp/stlastar.h +++ b/cpp/stlastar.h @@ -44,7 +44,7 @@ using namespace std; // Fixed size memory allocator can be disabled to compare performance // Uses std new and delete instead if you turn it off -#define USE_FSA_MEMORY 1 +#define USE_FSA_MEMORY 0 // disable warning that debugging information has lines that are truncated // occurs in stl headers