Knight-Path 1.0.D018

/home/archives/knight-path/branch.1/branch.0/delta5682.018/knight-path/solver/maze.cc

Go to the documentation of this file.
00001 //
00002 // knight-path - shortest knight path between two squares
00003 // Copyright (C) 2011 Peter Miller
00004 //
00005 // This program is free software; you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License as published by
00007 // the Free Software Foundation; either version 3 of the License, or (at
00008 // your option) any later version.
00009 //
00010 // This program is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // General Public License for more details.
00014 //
00015 // You should have received a copy of the GNU General Public License along
00016 // with this program. If not, see <http://www.gnu.org/licenses/>.
00017 //
00018 
00019 #include <deque>
00020 
00021 #include <knight-path/solver/maze.h>
00022 
00023 
00024 solver_maze::~solver_maze()
00025 {
00026 }
00027 
00028 
00029 solver_maze::solver_maze() :
00030     solver()
00031 {
00032 }
00033 
00034 
00035 solver_maze::pointer
00036 solver_maze::create(void)
00037 {
00038     return pointer(new solver_maze());
00039 }
00040 
00041 
00042 solver::list_t
00043 solver_maze::solve(const position &from, const position &to)
00044 {
00045     if (from == to)
00046     {
00047         // handle the trivial case
00048         list_t result;
00049         result.push_back(to);
00050         return result;
00051     }
00052 
00053     enum { unvisited = 99 };
00054 
00055     struct square
00056     {
00057         int distance;
00058         position predecessor;
00059 
00060         square() : distance(unvisited) { }
00061         square(int d, const position &ap) : distance(d), predecessor(ap) { }
00062     };
00063 
00064     //
00065     // We model the chass board by annotating it with the distances
00066     // between each square and the starting point, and also with the
00067     // next point backwards in the path.
00068     //
00069     // The board array could be replaced by a std::map<position> however this
00070     // comes with a penalty: O(log n) rather than O(1).  Conversely, the board
00071     // array means we need to be able to use the position::get_x and get_y
00072     // methods; a map would mean we could leave them private.  Today we choose
00073     // speed, and thus choose grope otherwise private members.
00074     //
00075     square board[8][8];
00076     board[from.get_y()][from.get_x()] = square(0, from);
00077 
00078     //
00079     // Starting from the "from" position, look for additional places
00080     // reachable by knight moves.
00081     //
00082     std::deque<position> work;
00083     work.push_back(from);
00084     while (!work.empty())
00085     {
00086         position p = work.front();
00087         work.pop_front();
00088         int dist = 1 + board[p.get_y()][p.get_x()].distance;
00089 
00090         // ask for a list or reachable positions
00091         position::list_t candidates = p.knight_moves();
00092 
00093         // check each possible knight move
00094         for
00095         (
00096             position::list_t::const_iterator it = candidates.begin();
00097             it != candidates.end();
00098             ++it
00099         )
00100         {
00101             square &b = board[it->get_y()][it->get_x()];
00102             if (b.distance == unvisited)
00103             {
00104                 work.push_back(*it);
00105                 b.distance = dist;
00106                 b.predecessor = p;
00107             }
00108             else
00109             {
00110                 //
00111                 // Because we always push new work onto the back of the queue,
00112                 // and we always pop the next job from the front of the
00113                 // queue, it means that the work queue is always ordered from
00114                 // shortest-path to longest-path.
00115                 //
00116                 assert(dist >= b.distance);
00117 #if 0
00118                 if (dist < b.distance)
00119                 {
00120                     b.distance = dist;
00121                     b.predecessor = p;
00122                 }
00123 #endif
00124             }
00125 
00126             //
00127             // The shortest-path implicit in the ordering means that when we
00128             // first reach the "to" square, we have found a shortest path --
00129             // there may be others as short, and there may be others longer, but
00130             // we can stop when we first reach the "to" square.
00131             //
00132             if (*it == to)
00133             {
00134                 work.clear();
00135                 break;
00136             }
00137         }
00138 
00139 #if 0
00140         //
00141         // For debug, print the in-progress board, annotated with distances
00142         //
00143         std::cerr << __FILE__ << ": " << __LINE__ << ": p = " << p << std::endl;
00144         for (int y = 7; y >= 0; --y)
00145         {
00146             std::cerr << "  +---+---+---+---+---+---+---+---+" << std::endl;
00147             std::cerr << (y + 1);
00148             std::cerr << " |";
00149             for (int x = 0; x < 8; ++x)
00150             {
00151                 const square &b = board[y][x];
00152                 std::cerr << ' ';
00153                 if (b.distance == unvisited)
00154                     std::cerr << ' ';
00155                 else
00156                     std::cerr << b.distance;
00157                 std::cerr << " |";
00158             }
00159             std::cerr << std::endl;
00160         }
00161         std::cerr << "  `---+---+---+---+---+---+---+---'" << std::endl;
00162         std::cerr << "    A   B   C   D   E   F   G   H" << std::endl;
00163 #endif
00164     }
00165 
00166     //
00167     // Build the answer by tracing the path backwards.
00168     //
00169     list_t result;
00170     position p = to;
00171     while (p != from)
00172     {
00173         square &b = board[p.get_y()][p.get_x()];
00174         assert(b.distance != unvisited);
00175         result.push_front(p);
00176         p = b.predecessor;
00177     }
00178     return result;
00179 }