#include #include #include #include "vector.h" #include "ctimer.h" // Owen Astrachan // Duke University // Computer Science Dept. // ola@cs.duke.edu // http://www.cs.duke.edu/~ola // // // // knight's tour, start on square of NxN board, visit all // squares with no repeat [no requirement that tour end a move // away from start] // // documentation is spotty // // two classes: standard knight tour and (sub class) smart tour // heuristic of smart tour : visit squares with fewest possible moves // out struct Move { int row; // record row index int col; // record column index }; // x,y changes for a night move, e.g,. -2,1 means 2 rows down, // 1 column over // Move AllMoveList [] = { {-2, 1}, // two up, one right {-2,-1}, // two up, one left { 2, 1}, // two down, one right { 2,-1}, // two down, one left { 1,-2}, // one down, two right { 1, 2}, // one down, two left {-1, 2}, // one up, two left {-1,-2} // one up, two right }; class KnightTour { public: KnightTour(int size); bool Solve(int row, int col); // return true if solution found // record move by writing to board position, unrecord move // and get a list of all moves virtual void RecordMove(int row, int col, int data); virtual void UnRecordMove(int row, int col, int data); virtual void GetMoves(int row, int col, Vector & moveList, int & numMoves); // return true if row,col are on board, false otherwise inline bool IsValidIndices(int row,int col); void Print() const; // print the board int NumMoves() const; // return # of moves made protected: const int NUM_MOVES = 8; const int MAXSIZE = 20; int mySize; int myBoard[MAXSIZE][MAXSIZE]; private: int myTotalMoves; int myMoveCount; }; class SmartTour : public KnightTour { public: SmartTour(int size); virtual void RecordMove(int row, int col, int data); virtual void UnRecordMove(int row, int col, int data); virtual void GetMoves(int row, int col, Vector & moveList, int & numMoves); private: int myNeighbors[MAXSIZE][MAXSIZE]; }; SmartTour::SmartTour(int size) : KnightTour(size) { int j,k,m; int row,col; for(j=0; j < mySize; j++) { for(k=0; k < mySize; k++) { myBoard[j][k] = 0; myNeighbors[j][k] = 0; // none accessible initially for(m=0; m < NUM_MOVES; m++) { row = j + AllMoveList[m].row; col = k + AllMoveList[m].col; if (IsValidIndices(row,col)) { myNeighbors[j][k]++; // add one for each neighbor } } } } } void SmartTour::GetMoves(int row, int col, Vector & moveList, int & numMoves) { int j,k; int nrow,ncol; numMoves = 0; Move nextMove; for(k=0; k < NUM_MOVES; k++) { nrow = row + AllMoveList[k].row; ncol = col + AllMoveList[k].col; nextMove.row = nrow; nextMove.col = ncol; if (IsValidIndices(nrow,ncol) && myBoard[nrow][ncol] == 0) { for(j=numMoves-1; j >= 0; j--) { if (myNeighbors[moveList[j].row][moveList[j].col] > myNeighbors[nextMove.row][nextMove.col]) { moveList[j+1] = moveList[j]; } else { break; } } moveList[j+1] = nextMove; numMoves++; } } } void SmartTour::RecordMove(int row, int col, int data) { int k; int nrow,ncol; for(k=0; k < NUM_MOVES; k++) { nrow = row + AllMoveList[k].row; ncol = col + AllMoveList[k].col; if (IsValidIndices(nrow,ncol) && myNeighbors[nrow][ncol]>=0) { myNeighbors[nrow][ncol] -= 1; } } myBoard[row][col] = data; } void SmartTour::UnRecordMove(int row, int col, int data) { int k; int nrow,ncol; for(k=0; k < NUM_MOVES; k++) { nrow = row + AllMoveList[k].row; ncol = col + AllMoveList[k].col; if (IsValidIndices(nrow,ncol) && myNeighbors[nrow][ncol]>=0) { myNeighbors[nrow][ncol] += 1; } } myBoard[row][col] = data; } KnightTour::KnightTour(int size) : mySize(size), myMoveCount(0), myTotalMoves(0) // precondition: 0 <= size < MAXSIZE // postcondition:KnightTour properly initialized { int j,k; // initialize board to all 'unvisited' for(j=0; j < mySize; j++) { for(k=0; k < mySize; k++) { myBoard[j][k] = 0; } } } void KnightTour::GetMoves(int row, int col, Vector & moveList, int & numMoves) { int k; int nrow,ncol; numMoves = 0; for(k=0; k < NUM_MOVES; k++) { nrow = row + AllMoveList[k].row; ncol = col + AllMoveList[k].col; if (IsValidIndices(nrow,ncol)) { moveList[numMoves].row = nrow; moveList[numMoves].col = ncol; numMoves++; } } } void KnightTour::RecordMove(int row, int col, int data) { myBoard[row][col] = data; } void KnightTour::UnRecordMove(int row, int col, int data) { myBoard[row][col] = data; } bool KnightTour::Solve(int row, int col) // postcondition: return 1 if tour solved, else return 0 { Vector moveList(NUM_MOVES); int numMoves; // check if move in range if (IsValidIndices(row,col)) { // check to see if already visited if (myBoard[row][col] != 0) { return false; } else { myMoveCount++; RecordMove(row,col,myMoveCount); myTotalMoves++; // increment # moves if (myMoveCount == mySize*mySize) // all squares seen? { return true; } // check all moves GetMoves(row,col,moveList,numMoves); int k; for(k=0; k < numMoves; k++) { if (Solve(moveList[k].row,moveList[k].col)) { return true; } } myMoveCount--; UnRecordMove(row,col,0); return false; } } else { return false; } } int KnightTour::NumMoves() const { return myTotalMoves; } bool KnightTour::IsValidIndices(int row, int col) { return 0 <= row && 0 <= col && row < mySize && col < mySize; } void KnightTour::Print() const // print the board (nicely justified) { int j,k; for(j=0; j < mySize;j++) { for(k=0; k < mySize; k++) { cout << setw(3) << myBoard[j][k]; } cout << endl; } } main(int argc, char *argv[]) { CTimer timer; int size = 5; if (argc > 1) { size = atoi(argv[1]); } KnightTour tour(size); timer.Start(); if (tour.Solve(0,0)) { tour.Print(); cout << "SOLVED: "; } else { cout << "NOT solved: "; } timer.Stop(); cout << tour.NumMoves() << " moves " << tour.NumMoves()/timer.ElapsedTime() << " moves per second" << endl; timer.Start(); SmartTour stour(size); if (stour.Solve(0,0)) { stour.Print(); cout << "SOLVED: "; } else { cout << "NOT solved: "; } timer.Stop(); cout << tour.NumMoves() << " moves " << tour.NumMoves()/timer.ElapsedTime() << " moves per second" << endl; }